it-swarm-es.tech

Patrón de fábrica. ¿Cuándo usar los métodos 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?

252
jjshell

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. Esta persona puede saber algo de la información que necesitan sobre las personas que contratan, 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 realmente contratar 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.

De la misma manera, el uso de Factory 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 saber acerca de las dependencias de la Cosa, excepto por los datos de cadena que provienen del consumidor.

363
kyoryu

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.

85
Rasmus Faber

Una situación en la que personalmente encuentro clases de Factory para tener sentido es cuando el objeto final que intenta crear se basa en varios otros objetos. Por ejemplo, en PHP: suponga que tiene un objeto House, que a su vez tiene un objeto Kitchen y LivingRoom, y el objeto LivingRoom también tiene un objeto TV.

El método más simple para lograr esto es hacer que cada objeto cree a sus hijos en su método de construcción, pero si las propiedades están relativamente anidadas, cuando su House falle en la creación, probablemente pasará algo de tiempo tratando de aislar exactamente lo que está fallando.

La alternativa es hacer lo siguiente (inyección de dependencia, si te 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 donde mirar, pero tener que usar esta parte cada vez que uno quiere una nueva House está lejos de ser conveniente. Entrar en 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 (ya que no es necesario crear y configurar cada dependencia individual cuando solo desea crear una House) y al mismo tiempo centralizado, lo que facilita el 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 pueden ser útiles las clases de fábrica.

67
Mahn

Es importante diferenciar claramente la idea detrás del método de fábrica o fábrica. Ambos están destinados a abordar diferentes tipos de problemas de creación de objetos.

Seamos específicos acerca del "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 que cuándo crear un objeto de funcionalidad (es) requerida (s) pero el tipo de objeto permanecerá indeciso o se decidirá por los parámetros dinámicos que se pasarán .

Ahora el punto es, se puede lograr aproximadamente lo mismo usando el patrón de fábrica en sí mismo, pero un gran inconveniente se introducirá 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) ser específico para alguna condición de negocios, así que en el futuro, cuando necesite ampliar la funcionalidad de su biblioteca para otras plataformas (técnicamente, necesita agregar más subclases de interfaz básica o clase abstracta para que Factory devuelva esos objetos también además de los existentes) basado en algunos parámetros dinámicos), entonces cada vez que necesite cambiar (extender) la lógica de la clase de fábrica, será una operación costosa y no buena desde la perspectiva del diseño. Por otro lado, si el patrón de "método de fábrica" ​​se usará para realizar lo mismo, entonces solo necesita crear una funcionalidad adicional (subclases) y registrarlo dinámicamente mediante inyección que no requiere cambios en su código base.

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();
    }
}
16
Prakash Chhipa

También son útiles cuando necesitas varios "constructores" con el mismo tipo de parámetro pero con un comportamiento diferente.

14
Rik

Es una buena idea usar métodos de fábrica dentro del objeto cuando:

  1. La clase de objeto no sabe qué subcategorías exactas tiene que crear.
  2. La clase de objeto está diseñada de modo que los objetos que crea fueron especificados por subclases
  3. La clase de objeto delega sus deberes a subclases auxiliares y no sabe qué clase exacta tomará estos deberes

Es una buena idea usar abstract factory class cuando:

  1. Su objeto no debe depender de cómo se crean y diseñan sus objetos internos
  2. El grupo de objetos vinculados debe usarse juntos y debe cumplir esta restricción
  3. El objeto debe ser configurado por una de las varias familias posibles de objetos vinculados que formarán parte de su objeto principal
  4. Es necesario compartir objetos secundarios que muestren interfaces solamente, pero no una implementación
13
Dzianis Yafimau

UML de

 enter image description here 

Producto: Define una interfaz de los objetos que crea el método Factory.

ConcreteProduct: Implementa la interfaz del producto

Creador: Declara el método de fábrica

ConcreateCreator: Implementa el método Factory para devolver una instancia de ConcreteProduct

Declaración del problema: Crea una fábrica de juegos usando 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 clase Factory implementando una FactoryMethod.

  1. Game es la interfaz para todo tipo de juegos. Define método complejo: createGame()

  2. Chess, Ludo, Checkers son diferentes variantes de juegos, que proporcionan implementación a createGame()

  3. public Game getGame(String gameName) es FactoryMethod en IGameFactory class

  4. GameFactory pre-crea diferentes tipos de juegos en el constructor. Implementa IGameFactory método de fábrica.

  5. nombre del juego se pasa como argumento de línea de comando a NotStaticFactoryDemo

  6. getGame en GameFactory acepta un nombre de juego y devuelve el objeto Game correspondiente.

Fábrica:

Crea objetos sin exponer la lógica de creación de instancias al cliente.

FactoryMethod

Defina una interfaz para crear un objeto, pero deje que las subclases decidan qué clase crear una instancia. El método Factory permite que una clase difiera la creación de instancias a subclases

Caso de uso:

Cuándo usar: Client no sabe qué clases concretas se requerirán para crear en tiempo de ejecución, pero solo quiere obtener una clase que haga el trabajo.

9
Ravindra babu

Es realmente una cuestión de gusto. 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 verificables, 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).

5
Brad Wilson

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 propiedades diferentes en el objeto devuelto, o cuando un tipo de fábrica específico está acoplado con su tipo concreto de retorno.

WCFusa las clases de ServiceHostFactory para recuperar objetos de ServiceHost en diferentes situaciones. El servicio estándar de servicio al cliente es usado por IIS para recuperar las instancias del servicio para .svc files, pero se usa un servicio WebScriptServiceHostFactory para los servicios que devuelven serializaciones a los clientes de 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 utilizar un método de fábrica dentro de esa clase.

4
Mark Cidade

Considere un escenario cuando tenga que diseñar una clase de orden y cliente. Por simplicidad y requisitos iniciales, no siente la necesidad de fábrica para la clase de orden y llene su solicitud con muchas declaraciones de 'nuevo pedido ()'. Las cosas están funcionando bien.

Ahora, un nuevo requisito entra en la imagen de que el objeto de pedido no se puede crear una instancia sin la asociación del cliente (nueva dependencia). Ahora tienes las siguientes consideraciones.

1- Creas una sobrecarga de constructor que funcionará solo para nuevas implementaciones. (Inaceptable). 2- Cambias las firmas de la Orden () y cambias cada invocación. (No es una buena práctica y un dolor real).

En cambio, si ha creado una fábrica para la clase de pedido, solo tiene que cambiar una línea de código y está listo para comenzar. Sugiero la clase de fábrica para casi todas las asociaciones agregadas. Espero que ayude.

3
Muhammad Awais

Según el sitio web de origen, sus intenciones son:

  • Defina una interfaz para crear un objeto, pero deje que las subclases decidan qué clase crear una instancia. Método de fábrica permite que una clase difiera la creación de instancias a subclases.

  • Definiendo un constructor "virtual".

  • El nuevo operador considera perjudicial.

Un ejemplo de cómo se puede utilizar:

abstract class AbstractFactoryMethod {
    abstract function makePHPBook($param);
}

class OReillyFactoryMethod extends AbstractFactoryMethod
{
    function makePHPBook($param)
    {
        $book = NULL;  
        switch ($param) {
            case "us":
                $book = new OReillyPHPBook();
            break;
            // Other classes...
            case "other":
                $book = new SamsPHPBook();
            break;
            default:
                $book = new OReillyPHPBook();
            break;        
    }

    return $book;
}

Y luego la prueba:

function testFactoryMethod($factoryMethodInstance)
{
    $phpUs = $factoryMethodInstance->makePHPBook("us");
    echo 'us php Author: '.$phpUs->getAuthor();
    echo 'us php Title: '.$phpUs->getTitle();
}

echo 'Testing OReillyFactoryMethod';
$factoryMethodInstance = new OReillyFactoryMethod();
testFactoryMethod($factoryMethodInstance);
3
Alexander Beat

Cualquier clase que aplace la creación del objeto a su subclase para el objeto con el que necesita trabajar puede verse como un ejemplo de patrón de fábrica.

He mencionado en detalle en otra respuesta en https://stackoverflow.com/a/49110001/504133

1
nits.kk

Yo 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 nombres lógicos como Numbers o Shapes. 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 las formas.

La fábrica utiliza el polimorfismo, la inyección de dependencia y la 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.

Digamos que está construyendo un sistema operativo o un marco y está construyendo 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 simple ejemplo. 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 );
0
Robert Rocha

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 en bruto, estos le permiten encapsular solo la lógica de construcción (y quizás 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, especialmente cuando no puedo reemplazar un ORM inadecuado y quiero instanciar de manera eficiente muchos objetos a partir de uniones de tabla DB o procedimientos almacenados.

0
jonfm

Ejemplo de AbstractFactory.

    TypeImpl<String> type = new TypeImpl<>();
    type.addType("Condition");
    type.addType("Hazardous");

    AbstractTypeFactory<String, Tag> tags = new AbstractTypeFactory<String, Tag>(type) {

        @Override
        public Tag create(String string) {
            String tp = type.find(string);

            switch (tp) {
                case "Hazardous":
                    return new HazardousTag();
                case "Condition":
                    return new ConditionTag();
                default:
                    return null;
            }
        }
    };

    Tag tagHazardous = tags.create("Hazardous");
    Tag tagCondition = tags.create("Condition");

}
0
Vahe Gharibyan

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;
      }
}
0
Samet öztoprak