Constructores de fábrica
Parte de la sección Programación Orientada a Objetos del Journey de Dart de Coddy — lección 14 de 110.
Un constructor de fábrica es un constructor especial que no siempre crea una nueva instancia. A diferencia de los constructores regulares, un constructor de fábrica puede devolver una instancia existente, un subtipo o incluso null (si es anulable). Se declara utilizando la palabra clave factory.
class Logger {
static final Logger _instance = Logger._internal();
// Constructor nombrado privado
Logger._internal();
// Constructor de fábrica
factory Logger() {
return _instance;
}
}
Logger log1 = Logger();
Logger log2 = Logger();
print(identical(log1, log2)); // trueEn este ejemplo, el constructor de fábrica siempre devuelve la misma instancia. No importa cuántas veces llames a Logger(), obtendrás el objeto idéntico. Este patrón se utiliza comúnmente para implementar singletons.
Los constructores factory se diferencian de los constructores regulares de una manera clave: no tienen acceso a this porque es posible que no creen una nueva instancia en absoluto. En su lugar, deben devolver explícitamente un objeto:
class Shape {
factory Shape(String type) {
if (type == 'circle') return Circle();
if (type == 'square') return Square();
return Rectangle();
}
}
class Circle extends Shape { Circle() : super._(); }
class Square extends Shape { Square() : super._(); }
class Rectangle extends Shape { Rectangle() : super._(); }Los constructores factory son útiles cuando necesitas control sobre la creación de instancias, como devolver objetos almacenados en caché, implementar pools de objetos o decidir qué subtipo instanciar basándose en los parámetros de entrada.
Desafío
FácilVamos a construir un sistema de notificaciones que utiliza constructores de fábrica para crear diferentes tipos de notificaciones basadas en una simple entrada de cadena.
Crearás dos archivos para organizar tu código:
notification.dart: Define una claseNotificationque sirva como base para todos los tipos de notificación. Tu clase debe tener:- Un campo
String message - Un constructor nombrado privado
Notification._internalque inicialice el mensaje - Un constructor de fábrica
Notification(String type, String message)que devuelva diferentes subtipos de notificación basados en el parámetrotype:- Si el tipo es
'email', devuelve unEmailNotification - Si el tipo es
'sms', devuelve unSMSNotification - De lo contrario, devuelve un
PushNotification
- Si el tipo es
- Un método
send()que imprimaSending: [message]
- Un campo
In el mismo archivo, crea tres subclases que extiendan Notification:
EmailNotification: Sobrescribesend()para imprimirEmail: [message]SMSNotification: Sobrescribesend()para imprimirSMS: [message]PushNotification: Sobrescribesend()para imprimirPush: [message]
Cada subclase necesita un constructor que reciba un message y lo pase al padre usando super._internal(message).
main.dart: Importa tu clase de notificación y crea tres notificaciones usando el constructor de fábrica:- Una notificación de correo electrónico con el mensaje
'Welcome!' - Una notificación de SMS con el mensaje
'Your code is 1234' - Una notificación push con el mensaje
'New update available'
send()en cada notificación en el orden indicado anteriormente.- Una notificación de correo electrónico con el mensaje
Salida esperada:
Email: Welcome!
SMS: Your code is 1234
Push: New update availableHoja de referencia
Un constructor de fábrica (factory constructor) es un constructor especial declarado con la palabra clave factory que no siempre crea una nueva instancia. A diferencia de los constructores regulares, puede devolver una instancia existente, un subtipo o null.
Los constructores de fábrica no tienen acceso a this y deben devolver explícitamente un objeto.
Ejemplo de patrón Singleton:
class Logger {
static final Logger _instance = Logger._internal();
Logger._internal(); // Private named constructor
factory Logger() {
return _instance; // Always returns same instance
}
}
Logger log1 = Logger();
Logger log2 = Logger();
print(identical(log1, log2)); // trueDevolver diferentes subtipos:
class Shape {
factory Shape(String type) {
if (type == 'circle') return Circle();
if (type == 'square') return Square();
return Rectangle();
}
}
class Circle extends Shape { Circle() : super._(); }
class Square extends Shape { Square() : super._(); }
class Rectangle extends Shape { Rectangle() : super._(); }Los constructores de fábrica son útiles para controlar la creación de instancias, como devolver objetos almacenados en caché, implementar pools de objetos o decidir qué subtipo instanciar en función de los parámetros de entrada.
Pruébalo tú mismo
import 'notification.dart';
void main() {
// TODO: Crear una notificación por correo electrónico con el mensaje 'Welcome!'
// TODO: Crear una notificación por SMS con el mensaje 'Your code is 1234'
// TODO: Crear una notificación push con el mensaje 'New update available'
// TODO: Llamar a send() en cada notificación en orden
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación Orientada a Objetos
1Fundamentos de POO
Archivos externosLibrerías e importacionesIntroducción a la POOClases vs. ObjetosLa palabra clave thisMétodosVariables de instanciaConceptos básicos de constructoresResumen - Calculadora sencilla4Seguridad de nulos
Introducción a la seguridad de nulosNulables vs No nulablesLos operadores ? y !Palabra clave Late y Null SafetyOperadores Null-awareNull Safety en clasesResumen - Sistema de perfil de usuario7Clases abstractas e interfaces
Clases abstractasMétodos abstractosInterfaces en DartInterfaces implícitasImplementar vs. ExtenderInterfaces múltiplesRepaso - Calculadora de figuras10Colecciones y Genéricos
Visión general de List, Set y MapColecciones con seguridad de tiposClases genéricasMétodos genéricosRestricciones genéricasIterable e IteratorResumen - Almacenamiento genérico2Constructores en Dart
Constructor por defectoConstructores nombradosListas de inicializaciónConstructores constantesConstructores de fábricaConstructores de redireccionamientoResumen - Constructor de formas5Encapsulamiento
Miembros públicos vs. privadosLa convención del prefijo _Privacidad a nivel de libreríaProfundidad en Getters y SettersOcultamiento de informaciónResumen - Registros de estudiantes8Mixins
Introducción a los MixinsCreación de MixinsUso de múltiples MixinsPalabra clave on en MixinsMixin vs HerenciaMixin vs InterfazResumen - Sistema Animal11Métodos especiales
Sobrescritura de toString()Sobrescritura de hashCode y ==Interfaz ComparableMétodo call()Sobrescritura de noSuchMethodResumen - Colección personalizada14Patrones de diseño Parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón ObserverPatrón Strategy3Propiedades de clase
Miembros de instancia vs. estáticosCampos Final y ConstVariables LateMétodos y campos estáticosGetters y SettersResumen - Gestor de cuentas bancarias6Herencia
Herencia básicaLa palabra clave superSobrescritura de métodosLa anotación @overrideLa palabra clave final en clasesConstructores y herenciaResumen - Jerarquía de empleados9Polimorfismo
Fundamentos del polimorfismoPolimorfismo mediante interfacesComprobación de tipos en tiempo de ejecuciónLos operadores is y asPalabra clave covariantResumen - Procesador de pagos12POO Asíncrona
Futures y async/awaitFundamentos de StreamsStream ControllersConstructores asíncronosAsincronía en métodos de claseRepaso - Data Fetcher15Patrones de diseño Parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón CompositePatrón Repository