Saltar a contenido

4.C Constructores y enumerados

Más de una forma de crear un objeto

A veces un objeto se puede crear de varias maneras: con unos valores por defecto, con datos propios o a partir de una «receta» ya preparada. Cada lenguaje lo resuelve a su manera.

Dart ofrece varias herramientas: parámetros posicionales opcionales entre corchetes con valor por defecto ([this.nombre = 'Margarita']), parámetros con nombre ({{String nombre = 'Margarita'}}) y constructores con nombre (Pizza.cuatroQuesos(...)), que sustituyen a la sobrecarga: Dart no permite dos constructores o dos métodos con el mismo nombre. Lo que no llega como parámetro se asigna en la lista de inicialización (: extras = 0), antes de ejecutar el cuerpo.

enum Tamano {
  pequena('Pequeña', 6),
  mediana('Mediana', 8),
  grande('Grande', 10);

  final String etiqueta;
  final int precioBase;
  const Tamano(this.etiqueta, this.precioBase);
}

class Pizza {
  final Tamano tamano;
  final String nombre;
  int extras;

  Pizza(this.tamano, [this.nombre = 'Margarita']) : extras = 0;

  Pizza.cuatroQuesos(this.tamano)
      : nombre = 'Cuatro quesos',
        extras = 3;

  void anadirExtra([int cantidad = 1]) {
    extras += cantidad;
  }

  int precio() => tamano.precioBase + extras;

  @override
  String toString() => 'Pizza($nombre, ${tamano.etiqueta}, $extras extras, ${precio()} €)';
}

void main() {
  final a = Pizza(Tamano.mediana);
  final b = Pizza.cuatroQuesos(Tamano.grande);
  final c = Pizza(Tamano.pequena, 'Barbacoa');
  print(a);
  print(b);
  print(c);
  a.anadirExtra();
  a.anadirExtra(2);
  print(a);
  print('total del pedido: ${a.precio() + b.precio() + c.precio()} €');
  print('tamaños: ${Tamano.values.map((t) => t.etiqueta).join(', ')}');
}

Salida:

Pizza(Margarita, Mediana, 0 extras, 8 €)
Pizza(Cuatro quesos, Grande, 3 extras, 13 €)
Pizza(Barbacoa, Pequeña, 0 extras, 6 €)
Pizza(Margarita, Mediana, 3 extras, 11 €)
total del pedido: 30 €
tamaños: Pequeña, Mediana, Grande

En este ejemplo hay tres formas de obtener una pizza: solo con el tamaño (el nombre por defecto es «Margarita»), con nombre propio (Barbacoa) y la receta ya preparada con un constructor alternativo (cuatroQuesos, que además trae tres extras). Después, anadirExtra se llama con y sin argumento.

Para un método que se puede llamar con o sin argumento, como anadirExtra() y anadirExtra(2), se usa un solo método con un parámetro opcional con valor por defecto ([int cantidad = 1]).

Un solo sitio para las reglas

Procura que todos los constructores acaben pasando por el mismo código (un constructor principal al que los demás llaman): así cualquier regla que añadas más adelante (por ejemplo, un máximo de extras) se escribe una sola vez.

Enumerados

Un enumerado es un tipo con un conjunto fijo y cerrado de valores: los días de la semana, los estados de un pedido, los tamaños de una pizza. Es mejor que usar números o textos sueltos porque el compilador impide valores inventados (Tamano.gigante no existe) y el código se lee solo.

Un enum en Dart puede tener campos, constructor y métodos, con constructor const. Tamano.values devuelve todos los valores en orden.

En el ejemplo, cada tamaño lleva datos asociados (la etiqueta que se muestra y el precio base), de modo que el resto del programa no necesita un if por cada tamaño: pregunta el dato al propio valor.

Errores frecuentes

Error Cómo evitarlo
Repetir las comprobaciones en cada constructor Un constructor principal y los demás que lo llaman
Dos constructores casi iguales con distinto orden de parámetros Usa valores por defecto o un método de fábrica con nombre claro
Usar números o textos para representar categorías (tipo = 1) Un enumerado
Cadenas de if/else según el valor de un enumerado Pon el dato o el comportamiento dentro del enumerado

Para practicar

Los constructores, los enumerados y la sobrecarga se practican en U4.6 · Prueba. Compara cómo se escribe en otro lenguaje con Pasar de uno a otro.