¿Cuándo es una buena idea usar métodos de fábrica dentro de un objeto en lugar de una clase de fábrica?
¿Cuándo es una buena idea usar métodos de fábrica dentro de un objeto en lugar de una clase de fábrica?
Respuestas:
Me gusta pensar en los patrones de diseño en términos de que mis clases sean 'personas', y los patrones son las formas en que las personas se comunican entre sí.
Entonces, para mí, el patrón de fábrica es como una agencia de contratación. Tienes a alguien que necesitará un número variable de trabajadores. Es posible que esta persona conozca la información que necesita sobre las personas que contrata, pero eso es todo.
Entonces, cuando necesitan un nuevo empleado, llaman a la agencia de contratación y les dicen lo que necesitan. Ahora, para contratar a alguien, necesita saber muchas cosas: beneficios, verificación de elegibilidad, etc. Pero la persona que contrata no necesita saber nada de esto: la agencia de contratación se encarga de todo eso.
Del mismo modo, el uso de una Fábrica permite al consumidor crear nuevos objetos sin tener que conocer los detalles de cómo se crean o cuáles son sus dependencias, solo tienen que proporcionar la información que realmente desean.
public interface IThingFactory
{
Thing GetThing(string theString);
}
public class ThingFactory : IThingFactory
{
public Thing GetThing(string theString)
{
return new Thing(theString, firstDependency, secondDependency);
}
}
Entonces, ahora el consumidor de ThingFactory puede obtener una Cosa, sin tener que conocer las dependencias de la Cosa, a excepción de los datos de cadena que provienen del consumidor.
within an object instead of a Factory class
. Creo que se refería al escenario en el que hace que el ctor sea privado y use un método estático para crear instancias de la clase (crear un objeto). Pero para seguir este ejemplo, primero se debe crear una instancia de la ThingFactory
clase para obtener Thing
objetos, lo que hace que esto sea Factory class
efectivo.
Los métodos de fábrica deben considerarse como una alternativa a los constructores, principalmente cuando los constructores no son lo suficientemente expresivos, es decir.
class Foo{
public Foo(bool withBar);
}
no es tan expresivo como:
class Foo{
public static Foo withBar();
public static Foo withoutBar();
}
Las clases de fábrica son útiles cuando necesita un proceso complicado para construir el objeto, cuando la construcción necesita una dependencia que no desea para la clase real, cuando necesita construir diferentes objetos, etc.
Una situación en la que personalmente encuentro clases separadas de Factory para que tenga sentido es cuando el objeto final que está tratando de crear se basa en varios otros objetos. Por ejemplo, en PHP: suponga que tiene un House
objeto, que a su vez tiene un Kitchen
y un LivingRoom
objeto, y el LivingRoom
objeto también tiene un TV
objeto dentro.
El método más simple para lograr esto es hacer que cada objeto cree sus hijos en su método de construcción, pero si las propiedades están relativamente anidadas, cuando House
falla la creación, probablemente pasará algún tiempo tratando de aislar exactamente lo que está fallando.
La alternativa es hacer lo siguiente (inyección de dependencia, si le gusta el término elegante):
$TVObj = new TV($param1, $param2, $param3);
$LivingroomObj = new LivingRoom($TVObj, $param1, $param2);
$KitchenroomObj = new Kitchen($param1, $param2);
$HouseObj = new House($LivingroomObj, $KitchenroomObj);
Aquí, si el proceso de creación de una House
falla, solo hay un lugar para buscar, pero tener que usar esta porción cada vez que se quiera una nueva House
está lejos de ser conveniente. Ingrese a las fábricas:
class HouseFactory {
public function create() {
$TVObj = new TV($param1, $param2, $param3);
$LivingroomObj = new LivingRoom($TVObj, $param1, $param2);
$KitchenroomObj = new Kitchen($param1, $param2);
$HouseObj = new House($LivingroomObj, $KitchenroomObj);
return $HouseObj;
}
}
$houseFactory = new HouseFactory();
$HouseObj = $houseFactory->create();
Gracias a la fábrica aquí, el proceso de creación de una House
se abstrae (en el sentido de que no es necesario crear y configurar cada dependencia cuando solo se desea crear una House
) y, al mismo tiempo, se centraliza, lo que facilita su mantenimiento. Hay otras razones por las que el uso de Fábricas separadas puede ser beneficioso (por ejemplo, la capacidad de prueba), pero encuentro este caso de uso específico para ilustrar mejor cómo las clases de Fábrica pueden ser útiles.
HouseFactory
clase?
create
método. Por ejemplo, si House
va a tener siempre el mismo tipo de LivingRoom
cosas, entonces puede tener sentido tener sus parámetros codificados en la clase de fábrica en lugar de pasarlos como argumentos. O es posible que desee proporcionar un type
argumento a su HouseFactory::create
método si tiene algunos tipos de LivingRoom
sy tiene un interruptor dentro con los parámetros codificados para cada tipo.
Es importante diferenciar claramente la idea detrás del uso de fábrica o método de fábrica. Ambos están destinados a abordar diferentes tipos de problemas de creación de objetos mutuamente excluyentes.
Seamos específicos sobre el "método de fábrica":
Lo primero es que, cuando está desarrollando una biblioteca o API que, a su vez, se utilizarán para un mayor desarrollo de aplicaciones, el método de fábrica es una de las mejores selecciones para el patrón de creación. Razón detrás; Sabemos cuándo crear un objeto de las funciones requeridas pero el tipo de objeto permanecerá indeciso o se decidirá si se pasan los parámetros dinámicos .
Ahora el punto es que se puede lograr aproximadamente lo mismo usando el patrón de fábrica en sí mismo, pero se introducirá un gran inconveniente en el sistema si el patrón de fábrica se usará para el problema resaltado anteriormente, es que su lógica de crear diferentes objetos (objetos de subclases) sea específico para alguna condición comercial, por lo que en el futuro cuando necesite ampliar la funcionalidad de su biblioteca para otras plataformas (en términos más técnicos, debe agregar más subclases de interfaz básica o clase abstracta para que la fábrica devuelva esos objetos también además de uno existente basado en algunos parámetros dinámicos), cada vez que necesite cambiar (ampliar) la lógica de la clase de fábrica, que será una operación costosa y no buena desde la perspectiva del diseño. Por otro lado, si "método de fábrica"
interface Deliverable
{
/*********/
}
abstract class DefaultProducer
{
public void taskToBeDone()
{
Deliverable deliverable = factoryMethodPattern();
}
protected abstract Deliverable factoryMethodPattern();
}
class SpecificDeliverable implements Deliverable
{
/***SPECIFIC TASK CAN BE WRITTEN HERE***/
}
class SpecificProducer extends DefaultProducer
{
protected Deliverable factoryMethodPattern()
{
return new SpecificDeliverable();
}
}
public class MasterApplicationProgram
{
public static void main(String arg[])
{
DefaultProducer defaultProducer = new SpecificProducer();
defaultProducer.taskToBeDone();
}
}
Es una buena idea usar métodos de fábrica dentro del objeto cuando:
Es una buena idea utilizar la clase abstracta de fábrica cuando:
UML de
Producto: define una interfaz de los objetos que crea el método Factory.
ConcreteProduct: implementa la interfaz del producto
Creador: declara el método Factory
ConcreateCreator: implementa el método Factory para devolver una instancia de ConcreteProduct
Planteamiento del problema: cree una Fábrica de juegos utilizando los Métodos de fábrica, que definen la interfaz del juego.
Fragmento de código:
import java.util.HashMap;
/* Product interface as per UML diagram */
interface Game{
/* createGame is a complex method, which executes a sequence of game steps */
public void createGame();
}
/* ConcreteProduct implementation as per UML diagram */
class Chess implements Game{
public Chess(){
}
public void createGame(){
System.out.println("---------------------------------------");
System.out.println("Create Chess game");
System.out.println("Opponents:2");
System.out.println("Define 64 blocks");
System.out.println("Place 16 pieces for White opponent");
System.out.println("Place 16 pieces for Black opponent");
System.out.println("Start Chess game");
System.out.println("---------------------------------------");
}
}
class Checkers implements Game{
public Checkers(){
}
public void createGame(){
System.out.println("---------------------------------------");
System.out.println("Create Checkers game");
System.out.println("Opponents:2 or 3 or 4 or 6");
System.out.println("For each opponent, place 10 coins");
System.out.println("Start Checkers game");
System.out.println("---------------------------------------");
}
}
class Ludo implements Game{
public Ludo(){
}
public void createGame(){
System.out.println("---------------------------------------");
System.out.println("Create Ludo game");
System.out.println("Opponents:2 or 3 or 4");
System.out.println("For each opponent, place 4 coins");
System.out.println("Create two dices with numbers from 1-6");
System.out.println("Start Ludo game");
System.out.println("---------------------------------------");
}
}
/* Creator interface as per UML diagram */
interface IGameFactory {
public Game getGame(String gameName);
}
/* ConcreteCreator implementation as per UML diagram */
class GameFactory implements IGameFactory {
HashMap<String,Game> games = new HashMap<String,Game>();
/*
Since Game Creation is complex process, we don't want to create game using new operator every time.
Instead we create Game only once and store it in Factory. When client request a specific game,
Game object is returned from Factory instead of creating new Game on the fly, which is time consuming
*/
public GameFactory(){
games.put(Chess.class.getName(),new Chess());
games.put(Checkers.class.getName(),new Checkers());
games.put(Ludo.class.getName(),new Ludo());
}
public Game getGame(String gameName){
return games.get(gameName);
}
}
public class NonStaticFactoryDemo{
public static void main(String args[]){
if ( args.length < 1){
System.out.println("Usage: java FactoryDemo gameName");
return;
}
GameFactory factory = new GameFactory();
Game game = factory.getGame(args[0]);
if ( game != null ){
game.createGame();
System.out.println("Game="+game.getClass().getName());
}else{
System.out.println(args[0]+ " Game does not exists in factory");
}
}
}
salida:
java NonStaticFactoryDemo Chess
---------------------------------------
Create Chess game
Opponents:2
Define 64 blocks
Place 16 pieces for White opponent
Place 16 pieces for Black opponent
Start Chess game
---------------------------------------
Game=Chess
Este ejemplo muestra una Factory
clase implementando a FactoryMethod
.
Game
es la interfaz para todo tipo de juegos. Define un método complejo:createGame()
Chess, Ludo, Checkers
son diferentes variantes de juegos, que proporcionan implementación para createGame()
public Game getGame(String gameName)
esta FactoryMethod
en IGameFactory
clase
GameFactory
crea previamente diferentes tipos de juegos en constructor. Implementa el IGameFactory
método de fábrica.
El nombre del juego se pasa como argumento de línea de comando a NotStaticFactoryDemo
getGame
in GameFactory
acepta un nombre de juego y devuelve el Game
objeto correspondiente .
Fábrica:
Crea objetos sin exponer la lógica de instanciación al cliente.
Método de fábrica
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 en subclases
Caso de uso:
Cuándo usarlo: Client
no sabe qué clases concretas será necesario crear en tiempo de ejecución, pero solo quiere obtener una clase que haga el trabajo.
getArea()
No es un método de fábrica en absoluto .
Es realmente una cuestión de gustos. Las clases de fábrica se pueden abstraer / interconectar según sea necesario, mientras que los métodos de fábrica son más livianos (y también tienden a ser comprobables, ya que no tienen un tipo definido, pero requerirán un punto de registro conocido, similar a un servicio localizador pero para localizar métodos de fábrica).
Las clases de fábrica son útiles para cuando el tipo de objeto que devuelven tiene un constructor privado, cuando diferentes clases de fábrica establecen diferentes propiedades en el objeto de retorno, o cuando un tipo de fábrica específico se combina con su tipo concreto de retorno.
WCF usa las clases ServiceHostFactory para recuperar objetos ServiceHost en diferentes situaciones. IIS utiliza el ServiceHostFactory estándar para recuperar instancias de ServiceHost para archivos .svc , pero se usa un WebScriptServiceHostFactory para servicios que devuelven serializaciones a clientes JavaScript. ADO.NET Data Services tiene su propio DataServiceHostFactory especial y ASP.NET tiene su ApplicationServicesHostFactory ya que sus servicios tienen constructores privados.
Si solo tiene una clase que consume la fábrica, puede usar un método de fábrica dentro de esa clase.
Considere un escenario cuando tenga que diseñar una clase de Pedido y Cliente. Por simplicidad y requisitos iniciales, no siente la necesidad de fábrica para la clase de pedido y llene su solicitud con muchas declaraciones de 'nuevo pedido ()'. Las cosas funcionan bien.
Ahora aparece un nuevo requisito de que el objeto Order no se puede instanciar sin la asociación del Cliente (nueva dependencia). Ahora tiene las siguientes consideraciones.
1- Se crea una sobrecarga del constructor que funcionará solo para nuevas implementaciones. (Inaceptable). 2- Cambia las firmas de Order () y cambia cada invocación. (No es una buena práctica y un verdadero dolor).
En cambio, si ha creado una fábrica para la Clase de orden, solo tiene que cambiar una línea de código y está listo para comenzar. Sugiero la clase Factory para casi todas las asociaciones agregadas. Espero que ayude.
si desea crear un objeto diferente en términos de uso. Es útil.
public class factoryMethodPattern {
static String planName = "COMMERCIALPLAN";
static int units = 3;
public static void main(String args[]) {
GetPlanFactory planFactory = new GetPlanFactory();
Plan p = planFactory.getPlan(planName);
System.out.print("Bill amount for " + planName + " of " + units
+ " units is: ");
p.getRate();
p.calculateBill(units);
}
}
abstract class Plan {
protected double rate;
abstract void getRate();
public void calculateBill(int units) {
System.out.println(units * rate);
}
}
class DomesticPlan extends Plan {
// @override
public void getRate() {
rate = 3.50;
}
}
class CommercialPlan extends Plan {
// @override
public void getRate() {
rate = 7.50;
}
}
class InstitutionalPlan extends Plan {
// @override
public void getRate() {
rate = 5.50;
}
}
class GetPlanFactory {
// use getPlan method to get object of type Plan
public Plan getPlan(String planType) {
if (planType == null) {
return null;
}
if (planType.equalsIgnoreCase("DOMESTICPLAN")) {
return new DomesticPlan();
} else if (planType.equalsIgnoreCase("COMMERCIALPLAN")) {
return new CommercialPlan();
} else if (planType.equalsIgnoreCase("INSTITUTIONALPLAN")) {
return new InstitutionalPlan();
}
return null;
}
}
Cualquier clase que difiera la creación del objeto a su subclase para el objeto con el que necesita trabajar puede verse como un ejemplo del patrón Factory.
He mencionado en detalle en otra respuesta en https://stackoverflow.com/a/49110001/504133
Creo que dependerá del grado de acoplamiento suelto que desee aportar a su código.
El método de fábrica desacopla muy bien las cosas, pero la clase de fábrica no.
En otras palabras, es más fácil cambiar las cosas si usa el método de fábrica que si usa una fábrica simple (conocida como clase de fábrica).
Mira en este ejemplo: https://connected2know.com/programming/java-factory-pattern/ . Ahora, imagina que quieres traer un nuevo animal. En la clase Factory, debe cambiar Factory, pero en el método factory, no, solo necesita agregar una nueva subclase.
Las clases de fábrica son más pesadas, pero te dan ciertas ventajas. En los casos en que necesita construir sus objetos a partir de múltiples fuentes de datos sin procesar, le permiten encapsular solo la lógica de construcción (y tal vez la agregación de datos) en un solo lugar. Allí se puede probar en abstracto sin preocuparse por la interfaz del objeto.
He encontrado que este es un patrón útil, particularmente cuando no puedo reemplazar un ORM inadecuado y quiero instanciar eficientemente muchos objetos de uniones de tablas DB o procedimientos almacenados.
Comparo las fábricas con el concepto de bibliotecas. Por ejemplo, puede tener una biblioteca para trabajar con números y otra para trabajar con formas. Puede almacenar las funciones de estas bibliotecas en directorios con nombre lógico como Numbers
oShapes
. Estos son tipos genéricos que podrían incluir enteros, flotadores, dobules, largos o rectángulos, círculos, triángulos, pentágonos en el caso de formas.
El petter de fábrica usa polimorfismo, inyección de dependencia e inversión de control.
El propósito declarado de los Patrones de Fábrica es: Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.
Entonces, supongamos que está creando un sistema operativo o marco y está creando todos los componentes discretos.
Aquí hay un ejemplo simple del concepto del Patrón de Fábrica en PHP. Puede que no esté al 100% en todo, pero está destinado a servir como un ejemplo simple. No soy un experto.
class NumbersFactory {
public static function makeNumber( $type, $number ) {
$numObject = null;
$number = null;
switch( $type ) {
case 'float':
$numObject = new Float( $number );
break;
case 'integer':
$numObject = new Integer( $number );
break;
case 'short':
$numObject = new Short( $number );
break;
case 'double':
$numObject = new Double( $number );
break;
case 'long':
$numObject = new Long( $number );
break;
default:
$numObject = new Integer( $number );
break;
}
return $numObject;
}
}
/* Numbers interface */
abstract class Number {
protected $number;
public function __construct( $number ) {
$this->number = $number;
}
abstract public function add();
abstract public function subtract();
abstract public function multiply();
abstract public function divide();
}
/* Float Implementation */
class Float extends Number {
public function add() {
// implementation goes here
}
public function subtract() {
// implementation goes here
}
public function multiply() {
// implementation goes here
}
public function divide() {
// implementation goes here
}
}
/* Integer Implementation */
class Integer extends Number {
public function add() {
// implementation goes here
}
public function subtract() {
// implementation goes here
}
public function multiply() {
// implementation goes here
}
public function divide() {
// implementation goes here
}
}
/* Short Implementation */
class Short extends Number {
public function add() {
// implementation goes here
}
public function subtract() {
// implementation goes here
}
public function multiply() {
// implementation goes here
}
public function divide() {
// implementation goes here
}
}
/* Double Implementation */
class Double extends Number {
public function add() {
// implementation goes here
}
public function subtract() {
// implementation goes here
}
public function multiply() {
// implementation goes here
}
public function divide() {
// implementation goes here
}
}
/* Long Implementation */
class Long extends Number {
public function add() {
// implementation goes here
}
public function subtract() {
// implementation goes here
}
public function multiply() {
// implementation goes here
}
public function divide() {
// implementation goes here
}
}
$number = NumbersFactory::makeNumber( 'float', 12.5 );
NumbersFactory::makeNumber( 'float', 12.5 );
me está dando más que decir new Float(12.5);
si sé que necesito un Float
? Esto es lo que no entiendo sobre las fábricas ... ¿cuál es el punto?