Patrón de método de fábrica
En la programación basada en clases, el patrón de método de fábrica es un patrón de creación que utiliza métodos de fábrica para abordar el problema de crear objetos sin tener que especificar la clase exacta del objeto que se creará.. Esto se hace creando objetos llamando a un método de fábrica, ya sea especificado en una interfaz e implementado por clases secundarias, o implementado en una clase base y opcionalmente anulado por clases derivadas, en lugar de llamar a un constructor.
Resumen
El método de fábrica patrón de diseño es uno de los veintitrés patrones de diseño conocidos que describen cómo resolver problemas de diseño recurrentes para diseñar software flexible y reutilizable orientado a objetos, es decir, objetos que son más fáciles de implementar, cambiar, probar y reutilizar.
El patrón de diseño Factory Method resuelve problemas como:
- ¿Cómo se puede crear un objeto para que las subclases puedan redefinir qué clase a instantánea?
- ¿Cómo puede una clase aplazar la instantánea a subclases?
El patrón de diseño de Factory Method describe cómo resolver tales problemas:
- Define una operación separada (método de fábrica) para crear un objeto.
- Crear un objeto llamando a un método de fábrica.
Esto permite escribir subclases para cambiar la forma en que se crea un objeto (para redefinir qué clase instanciar).
Véase también el diagrama de clases UML a continuación.
Definición
"Defina una interfaz para crear un objeto, pero deje que las subclases decidan qué clase instanciar. El método Factory permite que una clase difiera la creación de instancias que utiliza a las subclases." (Pandilla de cuatro)
La creación de un objeto a menudo requiere procesos complejos que no son apropiados para incluir dentro de un objeto de composición. La creación del objeto puede conducir a una duplicación significativa del código, puede requerir información a la que no puede acceder el objeto que lo compone, puede no proporcionar un nivel suficiente de abstracción o puede no ser parte de las preocupaciones del objeto que lo compone.. El patrón de diseño del método de fábrica maneja estos problemas definiendo un método separado para crear los objetos, que las subclases pueden anular para especificar el tipo de producto derivado que se creará.
El patrón del método de fábrica se basa en la herencia, ya que la creación de objetos se delega a las subclases que implementan el método de fábrica para crear objetos. Como se muestra en el siguiente ejemplo de C#, el patrón del método de fábrica también puede basarse en una interfaz, en este caso IPerson, para implementarse.
Estructura
Diagrama de clases UML
En el diagrama de clases UML anterior,
la clase Creator
que requiere un objeto Product
no instancia la clase Product1
directamente.
En cambio, Creator
se refiere a un factoryMethod()
separado para crear un objeto de producto,
lo que hace que el Creador
sea independiente de qué clase concreta se instancia.
Las subclases de Creator
pueden redefinir qué clase instanciar. En este ejemplo, la subclase Creator1
implementa el factoryMethod()
abstracto instanciando la clase Product1
.
Ejemplos
Esta implementación de C++14 se basa en la implementación anterior a C++98 del libro.
#include ■iostream#include - No.enum Producto Id {}MINE, TU};// define la interfaz de los objetos que el método de fábrica crea.clase Producto {}público: virtual vacío impresión() = 0; virtual ~Producto() = por defecto;};// implementa la interfaz de producto.clase ConcreteProductMINE: público Producto {}público: vacío impresión() {} std::Cout .. "esto=" .. esto .. "Imprimir MINE"n"; }};// implementa la interfaz de producto.clase Productos concretos: público Producto {}público: vacío impresión() {} std::Cout .. "esto=" .. esto .. "imprime el tuyo"n"; }};// declara el método de fábrica, que devuelve un objeto de tipo Producto.clase Creador {}público: virtual std::único_ptr.Producto■ crear()Producto Id id) {} si ()Producto Id::MINE == id) retorno std::make_unique.ConcreteProductMINE■(); si ()Producto Id::TU == id) retorno std::make_unique.Productos concretos■(); // repetir para los productos restantes... retorno nullptr; } virtual ~Creador() = por defecto;};int principal() {} // El único_ptr evita las fugas de memoria. std::único_ptr.Creador■ creador = std::make_unique.Creador■(); std::único_ptr.Producto■ producto = creador-crear()Producto Id::MINE); producto-impresión(); producto = creador-crear()Producto Id::TU); producto-impresión();}
La salida del programa es como
esto=0x6e5e90 impresión MINEesto=0x6e62c0 impresión TU
Un juego de laberinto se puede jugar en dos modos, uno con habitaciones regulares que solo están conectadas con habitaciones adyacentes y otro con habitaciones mágicas que permiten que los jugadores sean transportados al azar.
Estructura
Room
es la clase base para un producto final (MagicRoom
o OrdinaryRoom
). MazeGame
declara el método de fábrica abstracta para producir dicho producto base. MagicRoom
y OrdinaryRoom
son subclases del producto base que implementan el producto final. MagicMazeGame
y OrdinaryMazeGame
son subclases de MazeGame
que implementan el método de fábrica para producir los productos finales. Por lo tanto, los métodos de fábrica desacoplan a los llamadores (MazeGame
) de la implementación de las clases concretas. Esto hace que el "nuevo" Operador redundante, permite adherirse al principio Abierto/cerrado y flexibiliza el producto final en caso de cambio.
Implementaciones de ejemplo
C#
// Vocabulario vacío de objeto realpúblico interfaz IPerson{} cuerda GetName();}público clase Villager : IPerson{} público cuerda GetName() {} retorno "Village Person"; }}público clase CityPerson : IPerson{} público cuerda GetName() {} retorno "City Person"; }}público enum PersonType{} Rural, Urbana}/////// Implementación de Fábrica - Utilizado para crear objetos./// " Secundaria "público clase Factory{} público IPerson GetPerson()PersonType Tipo) {} interruptor ()Tipo) {} Caso PersonType.Rural: retorno nuevo Villager(); Caso PersonType.Urbana: retorno nuevo CityPerson(); por defecto: tiro nuevo NotSupportedException(); } }}
En el código anterior, puede ver la creación de una interfaz llamada IPerson
y dos implementaciones llamadas Villager
y CityPerson
. En función del tipo pasado al objeto Factory
, devolvemos el objeto concreto original como la interfaz IPerson
.
Un método de fábrica es solo una adición a la clase Factory
. Crea el objeto de la clase a través de interfaces pero, por otro lado, también permite que la subclase decida qué clase se instancia.
público interfaz IProduct{} cuerda GetName(); bool SetPrice()doble precio);}público clase Teléfono : IProduct{} privado doble _Precio; público cuerda GetName() {} retorno "Apple TouchPad"; } público bool SetPrice()doble precio) {} _Precio = precio; retorno verdadero; }}/* Casi igual que Fábrica, sólo una exposición adicional para hacer algo con el método creado */público abstracto clase ProductoAbstract Factory{} protegida abstracto IProduct Producto(); público IProduct Objeción() // Implementación del Método de Fábrica. {} retorno esto.Producto(); }}público clase PhoneConcreteFactory : ProductoAbstract Factory{} protegida Anulación IProduct Producto() {} IProduct producto = nuevo Teléfono(); // Haz algo con el objeto después de obtener el objeto. producto.SetPrice()20.30); retorno producto; }}
Puede ver que hemos usado MakeProduct
en concreteFactory. Como resultado, puede llamar fácilmente a MakeProduct()
para obtener el IProduct
. También puede escribir su lógica personalizada después de obtener el objeto en el método de fábrica concreto. El GetObject se hace abstracto en la interfaz de Factory.
Java
Este ejemplo de Java es similar al del libro Patrones de diseño.
El MazeGame usa Habitaciones pero pone la responsabilidad de crear Habitaciones a sus subclases que crean las clases concretas. El modo de juego regular podría usar este método de plantilla:
público abstracto clase 13.00 horas {} abstracto vacío conectar()13.00 horas habitación);}público clase MagicRoom extensiones 13.00 horas {} público vacío conectar()13.00 horas habitación) {}}público clase Habitación ordinaria extensiones 13.00 horas {} público vacío conectar()13.00 horas habitación) {}}público abstracto clase MazeGame {} privado final Lista.13.00 horas■ habitaciones = nuevo ArrayList■(); público MazeGame() {} 13.00 horas habitación1 = makeRoom(); 13.00 horas habitación2 = makeRoom(); habitación1.conectar()habitación2); habitaciones.añadir()habitación1); habitaciones.añadir()habitación2); } abstracto protegida 13.00 horas makeRoom();}
En el fragmento anterior, el constructor MazeGame
es un método de plantilla que tiene una lógica común. Se refiere al método de fábrica makeRoom
que encapsula la creación de salas de modo que otras salas se puedan usar en una subclase. Para implementar el otro modo de juego que tiene salas mágicas, basta con anular el método makeRoom
:
público clase MagicMazeGame extensiones MazeGame {} @Override protegida MagicRoom makeRoom() {} retorno nuevo MagicRoom(); }}público clase OrdinarioMazeGame extensiones MazeGame {} @Override protegida Habitación ordinaria makeRoom() {} retorno nuevo Habitación ordinaria(); }}MazeGame ordinario Juego = nuevo OrdinarioMazeGame();MazeGame magicGame = nuevo MagicMazeGame();
PHP
Otro ejemplo en PHP sigue, esta vez utilizando implementaciones de interfaz en lugar de subclases (sin embargo, lo mismo se puede lograr a través de subclases). Es importante tener en cuenta que el método de fábrica también puede definirse como público y llamarse directamente por el código del cliente (en contraste con el ejemplo anterior de Java).
/* interfaces de fábrica y coche */interfaz CarFactory{} público función makeCar(): Car;}interfaz Car{} público función # Tipo(): cuerda;}/* Implementaciones concretas de la fábrica y el coche */clase SedanFactory implementos CarFactory{} público función makeCar(): Car {} retorno nuevo Sedan(); }}clase Sedan implementos Car{} público función # Tipo(): cuerda {} retorno 'Sedan '; }}* Cliente */$factory = nuevo SedanFactory();Coche = $factory-makeCar();impresión Coche-# Tipo();
Pitón
Igual que el ejemplo de Java.
desde abc importación ABC, abstractoclase MazeGame()ABC): def __init_()auto) - Ninguno: auto.habitaciones = [] auto._prepare_rooms() def _prepare_rooms()auto) - Ninguno: habitación1 = auto.make_room() habitación2 = auto.make_room() habitación1.conectar()habitación2) auto.habitaciones.apéndice()habitación1) auto.habitaciones.apéndice()habitación2) def Jugar()auto) - Ninguno: impresión()f"Jugar usando {}auto.habitaciones[0]}") @abstractmethod def make_room()auto): aumento No Implemented Error()"¡Deberías implementar esto!")clase MagicMazeGame()MazeGame): def make_room()auto) - "Habitación Mágica": retorno MagicRoom()clase OrdinarioMazeGame()MazeGame): def make_room()auto) - "Habitación ordinaria": retorno Habitación ordinaria()clase 13.00 horas()ABC): def __init_()auto) - Ninguno: auto.conectado_rooms = [] def conectar()auto, habitación: "Habitación") - Ninguno: auto.conectado_rooms.apéndice()habitación)clase MagicRoom()13.00 horas): def __str__()auto) - str: retorno "Salón mágico"clase Habitación ordinaria()13.00 horas): def __str__()auto) - str: retorno "Habitación ordinaria"ordinario Juego = OrdinarioMazeGame()ordinario Juego.Jugar()magicGame = MagicMazeGame()magicGame.Jugar()
Usos
- En ADO.NET, IDbCommand.CreateParameter es un ejemplo del uso del método de fábrica para conectar jerarquías de clase paralelas.
- En Qt, QMainWindow::createPopupMenu es un método de fábrica declarado en un marco que puede ser anulado en el código de aplicación.
- En Java se utilizan varias fábricas en el paquete javax.xml.parsers. Por ejemplo javax.xml.parsers.DocumentBuilderFactory o javax.xml.parsers.SAXParserFactory.
- En el HTML5 DOM API, la interfaz de documento contiene un método de fábrica de creaciónElement para crear elementos específicos de la interfaz HTMLElement.
Contenido relacionado
ALGOL Y
Hacer bucle while
Filosofía de la inteligencia artificial
Red troncal
Historial de versiones de macOS