En la programación basada en clases , el patrón del método de fábrica es un patrón de creación que utiliza métodos de fábrica para tratar 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 .
Descripción general
El patrón de diseño Factory Method [1] es uno de los patrones de diseño "Gang of Four" que describen cómo resolver problemas de diseño recurrentes para diseñar software orientado a objetos flexible y reutilizable, es decir, objetos que son más fáciles de implementar, cambiar y probar. y reutilizar.
El patrón de diseño del Método de Fábrica se usa en lugar del constructor de clases regular para mantenerse dentro de los principios SOLID de programación, desacoplando la construcción de objetos de los objetos mismos. Esto tiene las siguientes ventajas y es útil para los siguientes casos, entre otros: [2]
- Permite la construcción de clases con un componente de un tipo que no ha sido predeterminado, sino que solo se define en una "interfaz", o que se define como un tipo dinámico.
- Así, por ejemplo, una clase
Vehicle
que tiene un miembroMotor
de interfazIMotor
, pero no un tipo concreto deMotor
definido de antemano, puede construirse diciéndole alVehicle
constructor que use unElectricMotor
o unGasolineMotor
. ElVehicle
código del constructor luego llama a un método de fábrica de motores, para crear el deseadoMotor
que cumpla con laIMotor
interfaz.
- Permite la construcción de subclases a un padre cuyo tipo de componente no ha sido predeterminado, pero solo definido en una interfaz, o que está definido como un tipo dinámico.
- Por ejemplo, una clase
Vehicle
con un miembroMotor
definido con un tipo dinámico, puede tener subclases de tipoElectricPlane
yOldCar
cada una construida con un tipo diferente de Motor. Esto se puede lograr construyendo las subclases con un método de fábrica de vehículos, mientras se suministra el tipo de motor. En casos como este, el constructor puede estar oculto.
- Permite un código más legible en los casos en que existen varios constructores, cada uno por un motivo diferente.
- Por ejemplo, si hay dos constructores
Vehicle(make:string, motor:number)
yVehicle(make:string, owner:string, license:number, purchased:date)
una construcción más legible de las clases sería usarVehicle.CreateOwnership(make:string, owner:string, license:number, purchased: date)
vsVehicle.Create(make:string, motor:number)
- Permite a una clase diferir la instanciación a subclases y evitar la instanciación directa de un objeto del tipo de clase principal.
- Por ejemplo, se puede evitar que se cree una instancia de un vehículo directamente ya que no tiene constructor, y solo se pueden crear subclases como ElectricPlane o OldCar llamando al método de fábrica Vehicle (estático) en el constructor o inicializador de subclase.
La creación de un objeto directamente dentro de la clase que requiere o usa el objeto es inflexible porque compromete la clase a un objeto en particular y hace que sea imposible cambiar la instanciación independientemente de la clase. Un cambio en el instanciador requeriría un cambio en el código de clase que preferiríamos no tocar. Esto se conoce como acoplamiento de código y el patrón del método de fábrica ayuda a desacoplar el código.
El patrón de diseño del método de fábrica se usa definiendo primero una operación separada, un método de fábrica , para crear un objeto, y luego usando este método de fábrica llamándolo para crear el objeto. Esto permite escribir subclases que deciden cómo se crea un objeto padre y qué tipo de objetos contiene el padre.
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 instanciación que usa a las subclases". ( Banda 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 dar lugar a una duplicación significativa del código, puede requerir información no accesible para el objeto que lo compone, puede no proporcionar un nivel suficiente de abstracción o puede que no sea 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, cuyas subclases pueden luego anular para especificar el tipo derivado de producto 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 subclases que implementan el método de fábrica para crear objetos. [3]
Estructura
Diagrama de clases UML
En el diagrama de clases de UML anterior , la Creator
clase que requiere un Product
objeto no crea una instancia de la Product1
clase directamente. En cambio, se Creator
refiere a un objeto separado factoryMethod()
para crear un producto, lo que hace que sea Creator
independiente de qué clase concreta se instancia. Las subclases de Creator
pueden redefinir qué clase instanciar. En este ejemplo, la Creator1
subclase implementa el resumen factoryMethod()
creando una instancia de la Product1
clase.
Ejemplo
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 salas mágicas que permiten que los jugadores sean transportados al azar.
Estructura
Room
es la clase base de un producto final ( MagicRoom
o OrdinaryRoom
). MazeGame
declara el método de fábrica abstracto para producir tal producto base. MagicRoom
y OrdinaryRoom
son subclases del producto base que implementan el producto final. MagicMazeGame
y OrdinaryMazeGame
son subclases de MazeGame
implementación del 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 sea redundante, permite la adherencia al principio Abierto / cerrado y hace que el producto final sea más flexible en caso de cambio.
Implementaciones de ejemplo
C#
// Vocabulario vacío de la interfaz pública del objeto actual IPerson { string GetName (); } público de clase Villager : IPerson { pública cadena GetName () { retorno "Persona pueblo" ; } } clase pública CityPerson : IPerson { public string GetName () { return "City Person" ; } }public enum PersonType { Rural , Urban }/// /// Implementation of Factory: se utiliza para crear objetos. /// public class Factory { public IPerson GetPerson ( PersonType tipo ) { switch ( tipo ) { case PersonType . Rural : volver nuevo aldeano (); caso PersonType . Urbano : devolver nueva CityPerson (); predeterminado : lanzar una nueva NotSupportedException (); } } }
En el código anterior puede ver la creación de una interfaz llamada IPerson
y dos implementaciones llamadas Villager
y CityPerson
. Según el tipo pasado al Factory
objeto, devolvemos el objeto concreto original como interfaz IPerson
.
Un método de fábrica es solo una adición a la Factory
clase. 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ública interfaz IProducto { cadena GetName (); bool SetPrice ( precio doble ); } teléfono de clase pública : IProduct { private double _price ; public string GetName () { return "Apple TouchPad" ; } public bool SetPrice ( precio doble ) { _price = price ; devuelve verdadero ; } } / * Casi lo mismo que Factory, solo una exposición adicional para hacer algo con el método creado * / public abstract class ProductAbstractFactory { protected abstract IProduct MakeProduct (); public IProduct GetObject () // Implementación del método de fábrica. { devuelve esto . MakeProduct (); } }público de clase PhoneConcreteFactory : ProductAbstractFactory { protegida anulación IProducto MakeProduct () { IProducto producto = nuevo teléfono (); // Haz algo con el objeto después de obtenerlo. producto . SetPrice ( 20.30 ); devolución de producto ; } }
Puede ver que lo hemos utilizado MakeProduct
en concreteFactory. Como resultado, puede llamar fácilmente MakeProduct()
desde él 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 Design Patterns .
MazeGame usa Rooms pero asigna la responsabilidad de crear Rooms a sus subclases que crean las clases concretas. El modo de juego normal podría usar este método de plantilla:
sala de clase abstracta pública { conexión vacía abstracta ( sala de la sala ); } público de clase MagicRoom extiende Habitación { público nula conexión ( Sala de habitación ) {} }public class OrdinaryRoom extiende la sala { public void connect ( sala de la sala ) {} }pública abstracta clase MazeGame { privado última lista < habitaciones > habitaciones = nuevo ArrayList <> (); pública MazeGame () { habitaciones room1 = makeRoom (); Habitación room2 = makeRoom (); habitación1 . conectar ( habitación2 ); habitaciones . añadir ( room1 ); habitaciones . agregar ( habitación2 ); } habitación protegida abstracta makeRoom (); }
En el fragmento anterior, el MazeGame
constructor es un método de plantilla que crea una lógica común. Se refiere al makeRoom
método de fábrica que encapsula la creación de habitaciones de modo que se puedan utilizar otras habitaciones en una subclase. Para implementar el otro modo de juego que tiene salas mágicas, basta con anular el makeRoom
método:
público de clase MagicMazeGame extiende MazeGame { @ Override protegida habitación makeRoom () { retorno nueva MagicRoom (); } }La clase pública OrdinaryMazeGame extiende MazeGame { @Override protected Room makeRoom () { return new OrdinaryRoom (); } }MazeGame ordinarioGame = nuevo OrdinaryMazeGame (); MazeGame magicGame = nuevo MagicMazeGame ();
PHP
A continuación, se muestra otro ejemplo en PHP , esta vez usando implementaciones de interfaz en lugar de subclases (sin embargo, se puede lograr lo mismo mediante subclases). Es importante tener en cuenta que el método de fábrica también puede definirse como público y ser llamado directamente por el código del cliente (en contraste con el ejemplo de Java anterior).
/ * Interfaces de fábrica y automóvil * /interfaz CarFactory { función pública makeCar () : Car ; } interfaz Car { public function getType () : string ; }/ * Implementaciones concretas de la fábrica y el coche * /la clase SedanFactory implementa CarFactory { función pública makeCar () : Car { return new Sedan (); } } class Sedan implementa Car { public function getType () : string { return 'Sedan' ; } }/ * Cliente * /$ factory = new SedanFactory (); $ coche = $ fábrica -> makeCar (); imprimir $ coche -> getType ();
Pitón
Igual que el ejemplo de Java.
de abc import ABC , método abstractoclass MazeGame ( ABC ): def __init__ ( self ) -> None : self . habitaciones = [] uno mismo . _prepare_rooms () def _prepare_rooms ( self ) -> Ninguno : room1 = self . make_room () habitación2 = self . hacer_habitación () habitación1 . conectar ( habitación2 ) self . habitaciones . append ( room1 ) auto . habitaciones . añadir ( habitación2 ) def play ( self ) -> None : print ( 'Reproducción usando el formato " {} "' . ( self . rooms [ 0 ])) @abstractmethod def make_room ( self ): raise NotImplementedError ( "¡Deberías implementar esto!" )clase MagicMazeGame ( MazeGame ): def make_room ( self ): return MagicRoom ()clase OrdinaryMazeGame ( MazeGame ): def make_room ( self ): return OrdinaryRoom ()class Room ( ABC ): def __init__ ( self ) -> None : self . habitaciones_conectadas = [] def connect ( self , room ) -> None : self . habitaciones_conectadas . añadir ( habitación )class MagicRoom ( Habitación ): def __str__ ( self ): return "Habitación mágica"class OrdinaryRoom ( Habitación ): def __str__ ( self ): return "Habitación ordinaria"ordinaryGame = OrdinaryMazeGame () ordinaryGame . 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 clases paralelas.
- En Qt , QMainWindow :: createPopupMenu es un método de fábrica declarado en un marco que puede anularse en el código de la 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 la API DOM de HTML5 , la interfaz de documento contiene un método de fábrica createElement para crear elementos específicos de la interfaz HTMLElement.
Ver también
- Design Patterns , el libro de gran influencia
- Patrón de diseño, descripción general de los patrones de diseño
- Patrón de fábrica abstracto , un patrón que a menudo se implementa utilizando métodos de fábrica.
- Patrón de constructor , otro patrón de creación
- Patrón de método de plantilla , que puede llamar a métodos de fábrica
- La idea de Joshua Bloch de un método de fábrica estático , que él dice no tiene equivalente directo en Design Patterns .
Referencias
- ^ Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides (1994). Patrones de diseño: elementos de software orientado a objetos reutilizable . Addison Wesley. págs. 107 y siguientes . ISBN 0-201-63361-2.CS1 maint: varios nombres: lista de autores ( enlace )
- ^ "El patrón de diseño del método de fábrica: problema, solución y aplicabilidad" . w3sDesign.com . Consultado el 17 de agosto de 2017 .
- ^ Freeman, Eric; Freeman, Elisabeth; Kathy, Sierra; Bert, Bates (2004). Hendrickson, Mike; Loukides, Mike (eds.). Head First Design Patterns (rústica) . 1 . O'REILLY. pag. 162. ISBN 978-0-596-00712-6. Consultado el 12 de septiembre de 2012 .
- ^ "El patrón de diseño del Método de Fábrica - Estructura y Colaboración" . w3sDesign.com . Consultado el 12 de agosto de 2017 .
- Martin Fowler ; Kent Beck ; John Brant ; William Opdyke ; Don Roberts (junio de 1999). Refactorización: mejora del diseño del código existente . Addison-Wesley. ISBN 0-201-48567-2.
- Gamma, Erich ; Helm, Richard ; Johnson, Ralph; Vlissides, John (1994). Patrones de diseño: elementos de software orientado a objetos reutilizable . Addison-Wesley. ISBN 0-201-63361-2.
- Cox, Brad J. (1986). Programación orientada a objetos: un enfoque evolutivo . Addison-Wesley. ISBN 978-0-201-10393-9.
- Cohen, Tal; Gil, José (2007). "Mejor construcción con fábricas" (PDF) . Revista de tecnología de objetos . Bertrand Meyer . 6 (6): 103. doi : 10.5381 / jot.2007.6.6.a3 . Consultado el 12 de marzo de 2007 .
enlaces externos
- Implementación de patrones de diseño de fábrica en Java
- Método de fábrica en UML y en LePUS3 (un lenguaje de descripción de diseño)
- Considere los métodos de fábrica estáticos de Joshua Bloch