it-swarm-pt.tech

Padrão de fábrica. Quando usar métodos de fábrica?

Quando é uma boa ideia usar métodos de fábrica dentro de um objeto em vez de uma classe Factory?

249
jjshell

Eu gosto de pensar em padrões de design em termos de minhas classes serem 'pessoas', e os padrões são as maneiras pelas quais as pessoas falam umas com as outras.

Então, para mim, o padrão de fábrica é como uma agência de contratação. Você tem alguém que precisará de um número variável de funcionários. Essa pessoa pode saber algumas informações que precisam nas pessoas que contratam, mas é só isso.

Então, quando precisam de um novo funcionário, ligam para a agência de contratação e dizem o que precisam. Agora, para realmente contratar alguém, você precisa conhecer muitas coisas - benefícios, verificação de elegibilidade, etc. Mas a pessoa que está contratando não precisa saber nada disso - a agência contratante lida com tudo isso.

Da mesma forma, o uso de uma Fábrica permite que o consumidor crie novos objetos sem precisar conhecer os detalhes de como eles são criados ou quais são suas dependências - eles só precisam fornecer as informações que realmente desejam.

public interface IThingFactory
{
    Thing GetThing(string theString);
}

public class ThingFactory : IThingFactory
{
    public Thing GetThing(string theString)
    {
        return new Thing(theString, firstDependency, secondDependency);
    }
}

Portanto, agora o consumidor do ThingFactory pode obter uma Thing, sem precisar saber sobre as dependências do Thing, exceto os dados de string que vêm do consumidor.

355
kyoryu

Os métodos de fábrica devem ser considerados como uma alternativa aos construtores - principalmente quando os construtores não são expressivos o suficiente, isto é.

class Foo{
  public Foo(bool withBar);
}

não é tão expressivo quanto:

class Foo{
  public static Foo withBar();
  public static Foo withoutBar();
}

As classes de fábrica são úteis quando você precisa de um processo complicado para construir o objeto, quando a construção precisa de uma dependência que você não deseja para a classe real, quando você precisa construir objetos diferentes etc.

84
Rasmus Faber

Uma situação em que eu pessoalmente encontro classes de Fábrica separadas para fazer sentido é quando o objeto final que você está tentando criar depende de vários outros objetos. Por exemplo, em PHP: Suponha que você tenha um objeto House, que por sua vez tem um objeto Kitchen e LivingRoom, e o objeto LivingRoom também possui um objeto TV

O método mais simples para conseguir isso é fazer com que cada objeto crie seus filhos em seu método de construção, mas se as propriedades estiverem relativamente aninhadas, quando a sua variável House falhar você provavelmente gastará algum tempo tentando isolar exatamente o que está falhando.

A alternativa é fazer o seguinte (injeção de dependência, se você gosta do termo chique):

$TVObj = new TV($param1, $param2, $param3);
$LivingroomObj = new LivingRoom($TVObj, $param1, $param2);
$KitchenroomObj = new Kitchen($param1, $param2);
$HouseObj = new House($LivingroomObj, $KitchenroomObj);

Aqui, se o processo de criação de uma House falhar, há apenas um lugar para procurar, mas ter que usar esse pedaço toda vez que alguém quiser uma nova House está longe de ser conveniente. Digite as 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();

Graças à fábrica aqui, o processo de criação de uma House é abstraído (em que você não precisa criar e configurar todas as dependências quando você quer criar uma House) e ao mesmo tempo centralizado, o que facilita a manutenção . Há outras razões pelas quais o uso de Fábricas separadas pode ser benéfico (por exemplo, capacidade de teste), mas eu acho esse caso de uso específico para ilustrar melhor como as classes de Fábrica podem ser úteis.

66
Mahn

É importante diferenciar claramente a idéia por trás do uso de fábrica ou método de fábrica. Ambos são destinados a resolver os diferentes tipos de problemas de criação de objetos mutuamente exclusivos.

Vamos ser específicos sobre "método de fábrica":

A primeira coisa é que, quando você estiver desenvolvendo biblioteca ou APIs, que por sua vez serão usadas para desenvolvimento de aplicativos adicionais, o método de fábrica é uma das melhores seleções para o padrão de criação. Razão por trás; Sabemos que quando criar um objeto de funcionalidade requerida (s), mas o tipo de objeto permanecerá indeciso ou será decidido por parâmetros dinâmicos sendo passados ​​.

Agora o ponto é, aproximadamente o mesmo pode ser alcançado usando o próprio padrão de fábrica mas uma grande desvantagem introduzirá no sistema se o padrão de fábrica for usado para o problema destacado acima, é que sua lógica de agrupar objetos diferentes (objetos de subclasses) ser específico para alguma condição de negócio, portanto, no futuro, quando você precisar estender a funcionalidade da sua biblioteca para outras plataformas (em mais tecnicamente, você precisa adicionar mais subclasses de interface básica ou classe abstrata para que a fábrica retorne esses objetos também com base em alguns parâmetros dinâmicos) toda vez que você precisar alterar (estender) a lógica da classe de fábrica que será de operação dispendiosa e não boa a partir da perspectiva do projeto. Por outro lado, se o padrão "factory method" ser usado para executar a mesma coisa, então você só precisa criar funcionalidade adicional (subclasses) e obtê-lo registrado dinamicamente por injeção que não requer mudanças no seu 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

É uma boa ideia usar métodos factory inside object quando:

  1. A classe do objeto não sabe quais subclasses exatas ela tem que criar
  2. A classe do objeto é projetada para que os objetos criados sejam especificados por subclasses
  3. A classe do objeto delega seus deveres para subclasses auxiliares e não sabe que classe exata assumirá esses deveres

É uma boa ideia usar abstract factory class quando:

  1. Seu objeto não deve depender de como seus objetos internos são criados e projetados
  2. O grupo de objetos vinculados deve ser usado em conjunto e você precisa atender a essa restrição
  3. O objeto deve ser configurado por uma das várias famílias possíveis de objetos vinculados que farão parte do seu objeto pai
  4. É necessário compartilhar objetos-filhos mostrando somente interfaces, mas não uma implementação
14
Dzianis Yafimau

Eles também são úteis quando você precisa de vários "construtores" com o mesmo tipo de parâmetro, mas com comportamento diferente. 

14
Rik

UML de 

enter image description here

Product: Define uma interface dos objetos que o método Factory cria.

ConcreteProduct: Implementa interface do produto

Criador: Declara o método de fábrica

ConcreateCreator: Implementa o método Factory para retornar uma instância de um ConcreteProduct

Problem statement: Crie uma Factory of Games usando o Factory Methods, que define a interface do jogo.

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");
        }           
    }
}

saída:

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 exemplo mostra uma classe Factory implementando um FactoryMethod.

  1. Game é a interface para todos os tipos de jogos. Define método complexo: createGame()

  2. Chess, Ludo, Checkers são variantes diferentes de jogos, que fornecem implementação para createGame()

  3. public Game getGame(String gameName) é FactoryMethod na classe IGameFactory

  4. GameFactory pré-cria diferentes tipos de jogos no construtor. Implementa o método factory IGameFactory

  5. o nome do jogo é passado como argumento de linha de comando para NotStaticFactoryDemo

  6. getGame em GameFactory aceita um nome de jogo e retorna o objeto Game correspondente.

Fábrica:

Cria objetos sem expor a lógica de instanciação ao cliente.

FactoryMethod

Defina uma interface para criar um objeto, mas deixe as subclasses decidirem qual classe instanciar. O método Factory permite que uma classe adie a instanciação para subclasses

Caso de uso:

Quando usar: Client não sabe quais classes concretas serão necessárias para criar no tempo de execução, mas apenas deseja obter uma classe que faça o trabalho.

9
Ravindra babu

É realmente uma questão de gosto. As classes de fábrica podem ser abstraídas/interconectadas conforme necessário, enquanto os métodos de fábrica são mais leves (e também tendem a ser testáveis, pois não possuem um tipo definido, mas exigirão um ponto de registro bem conhecido, semelhante a um serviço localizador, mas para localizar métodos de fábrica).

5
Brad Wilson

As classes de fábrica são úteis quando o tipo de objeto que eles retornam tem um construtor privado, quando diferentes classes de fábrica definem propriedades diferentes no objeto de retorno ou quando um tipo de fábrica específico é acoplado ao tipo de retorno de concreto. 

WCFusa classes ServiceHostFactory para recuperar objetos ServiceHost em diferentes situações. O ServiceHostFactory padrão é usado pelo IIS para recuperar instâncias do ServiceHost para arquivos .svc, mas um WebScriptServiceHostFactory é usado para serviços que retornam serializações para clientes JavaScript. O ADO.NET Data Services tem seu próprio DataServiceHostFactory especial e o ASP.NET tem seu ApplicationServicesHostFactory, já que seus serviços têm construtores privados.

Se você tem apenas uma classe que está consumindo a fábrica, pode usar um método de fábrica dentro dessa classe.

4
Mark Cidade

Considere um cenário quando você precisa criar uma classe Order e Customer. Para simplicidade e requisitos iniciais, você não sente necessidade de fábrica para a classe Order e preenche seu aplicativo com muitas instruções 'new Order ()'. As coisas estão funcionando bem.

Agora, um novo requisito entra em cena e o objeto Order não pode ser instanciado sem a associação do Cliente (nova dependência). Agora você tem as seguintes considerações.

1- Você cria uma sobrecarga de construtor que funcionará apenas para novas implementações. (Não aceitável). 2- Você altera as assinaturas Order () e altera cada e toda invocação. (Não é uma boa prática e uma dor real).

Em vez disso, se você criou uma fábrica para Classe de Pedido, você só precisa alterar uma linha de código e está pronto para ir. Sugiro classe de fábrica para quase todas as associações agregadas. Espero que ajude.

3
Muhammad Awais

De acordo com o site de criação de fontes, as intenções são:

  • Defina uma interface para criar um objeto, mas deixe as subclasses decidirem qual classe instanciar. O Factory Method permite que uma classe adie a instanciação para subclasses.

  • Definindo um construtor "virtual".

  • O novo operador considerado prejudicial.

Um exemplo de como isso pode ser usado:

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;
}

E então o teste:

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

Qualquer classe que adiar a criação do objeto para sua subclasse para o objeto com o qual ele precisa trabalhar pode ser vista como um exemplo do padrão Factory.

Eu mencionei em detalhes em outra resposta em https://stackoverflow.com/a/49110001/504133

1
nits.kk

Eu comparo as fábricas ao conceito de bibliotecas. Por exemplo, você pode ter uma biblioteca para trabalhar com números e outra para trabalhar com formas. Você pode armazenar as funções dessas bibliotecas em diretórios com nomes lógicos como Numbers ou Shapes. Esses são tipos genéricos que podem incluir inteiros, flutuantes, dobules, longs ou retângulos, círculos, triângulos, pentágonos no caso de formas.

O petter de fábrica utiliza polimorfismo, injeção de dependência e inversão de controle.

O objetivo declarado dos padrões de fábrica é: Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.

Então, digamos que você esteja construindo um Sistema Operacional ou Framework e esteja construindo todos os componentes discretos.

Aqui está um exemplo simples do conceito do Padrão de Fábrica no PHP. Eu posso não estar 100% em tudo isso, mas pretende servir como um exemplo simples. Eu não sou um especialista.

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

As classes de fábrica são mais pesadas, mas oferecem certas vantagens. Nos casos em que você precisa criar seus objetos a partir de várias fontes de dados brutas, eles permitem que você encapsule apenas a lógica de construção (e talvez a agregação dos dados) em um só lugar. Lá ele pode ser testado em resumo sem se preocupar com a interface do objeto.

Eu encontrei este um padrão útil, particularmente onde eu sou incapaz de substituir e ORM inadequado e deseja eficientemente instanciar muitos objetos de junções de tabela de banco de dados ou procedimentos armazenados.

0
jonfm

Exemplo 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

se você quiser criar um objeto diferente em termos de uso. É ú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