Construtores versus métodos de fábrica [fechado]

181

Ao modelar classes, qual é a maneira preferida de inicializar:

  1. Construtores ou
  2. Métodos de fábrica

E quais seriam as considerações para usar qualquer um deles?

Em certas situações, prefiro ter um método de fábrica que retorne nulo se o objeto não puder ser construído. Isso torna o código limpo. Posso simplesmente verificar se o valor retornado não é nulo antes de executar uma ação alternativa, em contraste com a exceção de um construtor. (Eu pessoalmente não gosto de exceções)

Digamos, eu tenho um construtor em uma classe que espera um valor de ID. O construtor usa esse valor para preencher a classe do banco de dados. No caso em que não existe um registro com o ID especificado, o construtor lança um RecordNotFoundException. Nesse caso, terei de incluir a construção de todas essas classes dentro de um bloco try..catch.

Em contraste com isso, posso ter um método de fábrica estático nessas classes que retornará nulo se o registro não for encontrado.

Qual abordagem é melhor nesse caso, construtor ou método de fábrica?

Hemanshu Bhojak
fonte

Respostas:

66

Na página 108 de Padrões de design: elementos de software orientado a objetos reutilizáveis ​​por Gamma, Helm, Johnson e Vlissides.

Use o padrão Factory Method quando

  • uma classe não pode antecipar a classe de objetos que deve criar
  • uma classe deseja que suas subclasses especifiquem os objetos que cria
  • classes delegam responsabilidade a uma das várias subclasses auxiliares e você deseja localizar o conhecimento de qual subclasse auxiliar é o delegado
Glenn
fonte
21
O método de fábrica estática é diferente do padrão de design do GoF - Padrão de Método de Fábrica. stackoverflow.com/questions/929021/…
Sree Rama
Não compare com o padrão de método Factory dos padrões de design do GoF.
Sree Rama
137
isso não me explica nada
Sushant
@ Shanus, por que é assim?
PaulD 01/10/14
2
Esta resposta não responde à pergunta, apenas transmite informações para ler para entender / explicar o conceito ... Isso é mais um comentário.
Crt
202

Pergunte a si mesmo o que são e por que as temos. Ambos estão lá para criar a instância de um objeto.

ElementarySchool school = new ElementarySchool();
ElementarySchool school = SchoolFactory.Construct(); // new ElementarySchool() inside

Nenhuma diferença até agora. Agora imagine que temos vários tipos de escola e queremos passar do ElementarySchool para o HighSchool (que é derivado de um ElementarySchool ou implementa a mesma interface ISchool que o ElementarySchool). A alteração do código seria:

HighSchool school = new HighSchool();
HighSchool school = SchoolFactory.Construct(); // new HighSchool() inside

No caso de uma interface, teríamos:

ISchool school = new HighSchool();
ISchool school = SchoolFactory.Construct(); // new HighSchool() inside

Agora, se você tiver esse código em vários locais, poderá ver que o uso do método factory pode ser bem barato, porque uma vez que você altera o método factory (se usarmos o segundo exemplo com interfaces).

E esta é a principal diferença e vantagem. Quando você começa a lidar com hierarquias de classe complexas e deseja criar dinamicamente uma instância de uma classe a partir dessa hierarquia, obtém o código a seguir. Os métodos de fábrica podem usar um parâmetro que diz ao método qual instância concreta instanciar. Digamos que você tenha uma classe MyStudent e precise instanciar o objeto ISchool correspondente para que seu aluno seja membro dessa escola.

ISchool school = SchoolFactory.ConstructForStudent(myStudent);

Agora você tem um lugar no aplicativo que contém a lógica comercial que determina qual objeto ISchool instanciar para diferentes objetos IStudent.

Portanto - para classes simples (objetos de valor, etc.), o construtor é bom (você não deseja sobrecarregar seu aplicativo), mas para hierarquias de classes complexas o método de fábrica é a maneira preferida.

Dessa forma, você segue o primeiro princípio de design da turma dos quatro livros "Programa para uma interface, não uma implementação".

David Pokluda
fonte
2
Mesmo quando você pensa que é uma classe simples, há uma chance de alguém precisar estender sua classe simples, portanto o método de fábrica ainda é melhor. Por exemplo, você pode começar com o ElementarySchool, mas mais tarde alguém (incluindo você) pode estendê-lo com PrivateElementarySchool e PublicElementarySchool.
jack
10
esta deve ser a resposta aceita
am05mhz
2
@ David, boa resposta, mas você pode expandir um exemplo em que cada implementação de interface pode exigir parâmetros diferentes para a construção. Aqui está um exemplo bobo: IFood sandwich = new Sandwich(Cheese chz, Meat meat);e IFood soup = new Soup(Broth broth, Vegetable veg);Como a fábrica e / ou o construtor podem ajudar aqui?
Brian
1
Acabei de ler três outras explicações sobre o objetivo do uso da fábrica, e essa foi a que finalmente fez com que "clique" para mim. Obrigado!
Daniel Peirano
Por que essa resposta não é aceita?
Tomas
74

Você precisa ler (se tiver acesso a) Java Efetivo 2 Item 1: Considere métodos estáticos de fábrica em vez de construtores .

Métodos de fábrica estática vantagens:

  1. Eles têm nomes.
  2. Eles não são obrigados a criar um novo objeto cada vez que são chamados.
  3. Eles podem retornar um objeto de qualquer subtipo do tipo de retorno.
  4. Eles reduzem a verbosidade da criação de instâncias de tipo com parâmetros.

Métodos estáticos de fábrica desvantagens:

  1. Ao fornecer apenas métodos estáticos de fábrica, as classes sem construtores públicos ou protegidos não podem ser subclassificadas.
  2. Eles não são facilmente distinguíveis de outros métodos estáticos
cherouvim
fonte
4
Isso me parece um bug grave em Java, depois um problema geral de OOD. Existem inúmeras linguagens OO que nem têm construtores, mas a subclassificação funciona bem.
Jörg W Mittag
1
@cherouvim porque na maior parte do código é escrito usando Construtores se( factory methods are better than Constructors. ( Item-1 ) ) Effective java
Asif Mushtaq
Bons pontos. É específico para Java. Pode-se argumentar sobre um recurso de linguagem que diferencia os métodos de fábrica de outros métodos estáticos.
OCDev
30

Por padrão, os construtores devem ser preferidos, porque são mais simples de entender e escrever. No entanto, se você precisar especificamente dissociar as sutilezas de construção de um objeto de seu significado semântico, conforme entendido pelo código do cliente, seria melhor usar fábricas.

A diferença entre construtores e fábricas é análoga a, digamos, uma variável e um ponteiro para uma variável. Há outro nível de indireção, que é uma desvantagem; mas também há outro nível de flexibilidade, o que é uma vantagem. Portanto, ao fazer uma escolha, você deve fazer essa análise de custo versus benefício.

Frederick The Fool
fonte
17
Então, (estilo TDD), você começaria com os construtores como a maneira mais simples de realizar o trabalho. E depois refatorar para fábricas quando você começar a obter odores de código (como lógica condicional repetida que determina qual construtor chamar)?
AndyM 11/03/10
1
Ponto muito importante. Um estudo de usuário comparando fábricas e construtores encontrou resultados altamente significativos indicando que as fábricas são prejudiciais à usabilidade da API: "os usuários precisam de muito mais tempo (p = 0,005) para construir um objeto com uma fábrica do que com um construtor" [The Factory Pattern in API Design : Uma avaliação de usabilidade ].
mdeff
12

Use uma fábrica somente quando precisar de controle extra com a criação de objetos, de uma maneira que não poderia ser feita com construtores.

As fábricas têm a possibilidade de armazenar em cache, por exemplo.

Outra maneira de usar fábricas é em um cenário em que você não sabe o tipo que deseja construir. Muitas vezes, você vê esse tipo de uso nos cenários de fábrica de plug-ins, nos quais cada plug-in deve derivar de uma classe base ou implementar algum tipo de interface. A fábrica cria instâncias de classes derivadas da classe básica ou que implementam a interface.

Patrick Peters
fonte
11

Uma citação de "Java Efetivo", 2ª ed., Item 1: Considere métodos estáticos de fábrica em vez de construtores, p. 5:

"Observe que um método de fábrica estático não é o mesmo que o padrão de Método de Fábrica dos Padrões de Design [Gamma95, p. 107]. O método de fábrica estático descrito neste item não tem equivalente direto nos Padrões de Design."

Eugen Labun
fonte
10

Além de "java eficaz" (como mencionado em outra resposta), outro livro clássico também sugere:

Prefira métodos estáticos de fábrica (com nomes que descrevam os argumentos) a construtores sobrecarregados.

Por exemplo. não escreva

Complex complex = new Complex(23.0);

mas escreva

Complex complex = Complex.fromRealNumber(23.0);

O livro chega ao ponto de sugerir tornar o Complex(float)construtor privado, para forçar o usuário a chamar o método de fábrica estática.

nota azul
fonte
2
Lendo essa parte do livro me trouxe aqui
Purple Haze
1
@ Bayrem: eu também, eu estava relendo recentemente e achei que deveria adicioná-lo às respostas.
blue_note
1
Em uma nota relacionada, você pode achar útil algumas convenções de nomenclatura elaborada pela java.time quadro sobre a nomeação de from…, to…, parse…, with…, e assim por diante. Lembre-se de que as classes java.time são criadas para serem imutáveis, mas algumas dessas convenções de nomenclatura também podem ser úteis para as classes mutáveis.
Basil Bourque
7

Um exemplo concreto de um aplicativo CAD / CAM.

Um caminho de corte seria feito usando um construtor. É uma série de linhas e arcos que definem um caminho a ser cortado. Embora a série de linhas e arcos possa ser diferente e ter coordenadas diferentes, ela é facilmente manipulada passando uma lista para um construtor.

Uma forma seria feita usando uma fábrica. Porque, enquanto houver uma classe de forma, cada forma será configurada de maneira diferente, dependendo do tipo de forma. Não sabemos que formato vamos inicializar até que o usuário faça uma seleção.

RS Conley
fonte
5

Digamos, eu tenho um construtor em uma classe que espera um valor de ID. O construtor usa esse valor para preencher a classe do banco de dados.

Esse processo definitivamente deve estar fora de um construtor.

  1. O construtor não deve acessar o banco de dados.

  2. A tarefa e o motivo de um construtor é inicializar membros de dados e estabelecer invariantes de classe usando valores passados ​​para o construtor.

  3. Para todo o resto, uma abordagem melhor é usar o método de fábrica estática ou, em casos mais complexos, uma fábrica ou classe de construtor separada .

Algumas linhas de guia do construtor da Microsoft :

Faça um trabalho mínimo no construtor. Os construtores não devem fazer muito trabalho além de capturar os parâmetros do construtor. O custo de qualquer outro processamento deve ser adiado até ser necessário.

E

Considere usar um método estático de fábrica em vez de um construtor se a semântica da operação desejada não for mapeada diretamente para a construção de uma nova instância.

Homem leve
fonte
2

Às vezes, você precisa verificar / calcular alguns valores / condições ao criar um objeto. E se ele pode lançar uma exceção - o constructro é uma maneira muito ruim. Então, você precisa fazer algo assim:

var value = new Instance(1, 2).init()
public function init() {
    try {
        doSome()
    }
    catch (e) {
        soAnotherSome()
    }
}

Onde todos os cálculos adicionais estão em init (). Mas somente você, como desenvolvedor, realmente conhece esse init (). E é claro, depois de meses, você simplesmente esquece. Mas se você tem uma fábrica - faça tudo o que precisa em um método para ocultar esse init () da chamada direta - para que não haja problemas. Com essa abordagem, não há problemas em cair na criação e no vazamento de memória.

Alguém lhe falou sobre cache. É bom. Mas você também deve se lembrar do padrão Flyweight, que é bom de usar com o modo Factory.

gerador de lixo
fonte