Enums mejorados en Dart como fábricas: adiós a los switch de constructores
Desde Dart 2.15 los constructores son funciones de primera clase; desde 2.17 los enums admiten campos y constructores const. Juntos convierten cada valor en una fábrica que se instancia sola.

Dart permite desde la versión 2.15 pasar constructores como referencias de primera clase y, desde la 2.17, declarar enums con campos, métodos y constructores constantes. Juntando las dos cosas, cada valor de un enum puede guardar el constructor de su propia clase y ejercer de fábrica polimórfica. El switch que normalmente se mantiene al lado desaparece.
La pieza llegó en dos entregas. La primera fueron los constructor tearoffs: antes, para pasar un constructor como función, había que envolverlo en un lambda; ahora basta con escribir EmailNotification.new o User.fromJson y el constructor es una clausura más. La segunda fueron los enums mejorados, que dejaron de ser enteros con nombre y pasaron a admitir campos final, constructores const, mixins, interfaces, getters y sobrecarga de operadores.
El patrón
La gracia está en declarar el enum con un campo cuyo tipo es la firma de la función que crea el objeto, y pasar el tearoff en cada valor:
enum DocumentType {
pdf(PdfDocument.new),
markdown(MarkdownDocument.new),
html(HtmlDocument.new);
final Document Function(String title) create;
const DocumentType(this.create);
}
Como el constructor del enum es constante, todo el conjunto sigue siendo constante en tiempo de compilación. Instanciar es llamar al campo: DocumentType.markdown.create('notas.md').render(). Sin switch, sin búsquedas en un mapa y sin reflexión.
Donde se nota
El caso que mejor lo aprovecha es la deserialización de JSON polimórfico: webhooks, eventos de analítica o notificaciones push que llegan con un campo de tipo y un payload distinto según el caso. En lugar de un switch de parseo de cuarenta líneas, el enum mapea directamente la cadena entrante al constructor con nombre correspondiente, del estilo login(LoginPayload.fromJson) con un campo EventPayload Function(Map<String, dynamic>).
La ventaja de fondo no es la brevedad, es el compilador. Si alguien añade un valor nuevo al enum, se ve obligado a darle su tearoff en el mismo sitio. No hay forma de olvidarse de actualizar una rama del switch en otro archivo, ni de dejar un registro mutable desincronizado con los tipos que dice manejar.
El precio es acoplamiento: el enum deja de ser una lista neutral de identificadores y pasa a depender de las clases concretas que representa. En una capa de presentación o de transporte, eso es exactamente lo que quieres. En un módulo de dominio que debe ignorar quién implementa la interfaz, es lo contrario. El patrón no necesita paquetes ni generación de código, así que la decisión es de diseño y se puede tomar enum a enum.


