DMarlon Studio

Lección 2 · 2 de 20

Constructores con nombre y factory · Dart

Cree objetos de varias formas en Dart: constructores con nombre, redirigidos y privados, y constructores factory que validan o reutilizan objetos.

Dart · Constructores con nombre y factory en DMarlon Studio: el código del ejemplo y su salida en la terminal

Una clase de Dart puede tener varios constructores; los que no son el principal se llaman constructores con nombre y se escriben Clase.nombre. Currency._ es uno de ellos y, como empieza por guion bajo, es privado: solo el propio archivo puede llamarlo. Es el que construye de verdad los objetos, y escribe (new Currency ...) cada vez para que la salida muestre cuándo se crea uno.

Currency.euro() es un constructor redirigido: detrás de los dos puntos, this._('EUR', 2) delega en otro constructor de la misma clase. Un constructor normal, con nombre o sin él, siempre crea un objeto nuevo; por eso Currency.euro() escribe (new Currency EUR).

Un constructor `factory` no está obligado a crear nada: es una función que devuelve una instancia, nueva o ya existente. Currency(String code) busca en el mapa estático _cache con putIfAbsent y solo construye la moneda la primera vez que se pide. Por eso Currency('USD') y Currency.parse(' usd ') devuelven el mismo objeto, como confirma identical, y USD se crea una sola vez.

Un factory también sirve para validar o convertir antes de construir. Currency.parse limpia el texto, lanza FormatException si el código no tiene tres letras y, si es correcto, delega en el factory principal. Por último, format tiene en cuenta los decimales de cada moneda: el yen (JPY) no tiene, así que 1500 se escribe tal cual.

Código · main.dart

class Currency {  final String code;  final int decimals;  static final Map<String, Currency> _cache = {};  // Private named constructor: the one that really builds objects.  Currency._(this.code, this.decimals) { print('  (new Currency $code)'); }  // Named constructor that redirects to another one.  Currency.euro() : this._('EUR', 2);  // A factory may return an existing object instead of a new one.  factory Currency(String code) =>      _cache.putIfAbsent(code, () => Currency._(code, code == 'JPY' ? 0 : 2));  // A factory can also validate before building anything.  factory Currency.parse(String text) {    final code = text.trim().toUpperCase();    if (code.length != 3) throw FormatException('Bad currency code "$text"');    return Currency(code);  }  String format(int units) => decimals == 0      ? '$units $code'      : '${(units / 100).toStringAsFixed(decimals)} $code';}void main() {  final dollar = Currency('USD');  print('Same object: ${identical(dollar, Currency.parse(' usd '))}');  print(Currency('JPY').format(1500));  print(Currency.euro().format(1999));  try { Currency.parse('euro'); } on FormatException catch (e) { print(e.message); }}

Salida · dart run main.dart

  (new Currency USD)
Same object: true
  (new Currency JPY)
1500 JPY
  (new Currency EUR)
19.99 EUR
Bad currency code "euro"

Para recordar

  • Una clase puede tener tantos constructores con nombre como necesite.
  • : this(...) redirige a otro constructor de la misma clase.
  • factory puede devolver un objeto que ya existe, por ejemplo, de una caché.
  • Un constructor privado (Clase._) obliga a pasar por los factory.
  • identical comprueba si dos variables apuntan al mismo objeto.

Ejercicio

Añada un factory Currency.fromSymbol(String symbol) que convierta '€' en EUR y '£' en GBP, y que lance FormatException con cualquier otro símbolo.