Quando é uma boa ideia usar métodos de fábrica dentro de um objeto em vez de uma classe Factory?
Quando é uma boa ideia usar métodos de fábrica dentro de um objeto em vez de uma classe Factory?
Respostas:
Gosto de pensar em pais de design em termos de minhas pessoas serem 'pessoas', e os padrões são as maneiras pelas quais as pessoas conversam entre si.
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 trabalhadores. Essa pessoa pode conhecer algumas informações de que precisa nas pessoas que contrata, mas é isso.
Portanto, quando precisam de um novo funcionário, ligam para a agência contratada e dizem o que precisam. Agora, para contratar alguém, você precisa conhecer muitas coisas - benefícios, verificação de elegibilidade etc. Mas a pessoa que contrata não precisa saber nada disso - a agência de contratação lida com tudo isso.
Da mesma forma, o uso de uma fábrica permite que o consumidor crie novos objetos sem precisar saber 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 da ThingFactory pode obter uma coisa, sem ter que saber sobre as dependências da coisa, exceto os dados da cadeia de caracteres que vem do consumidor.
within an object instead of a Factory class
. Eu acho que ele quis dizer o cenário em que você torna o ctor privado e usa um método estático para instanciar a classe (criar um objeto). Mas, para seguir este exemplo, é preciso instanciar a ThingFactory
classe primeiro para obter Thing
objetos, o que torna isso Factory class
efetivo.
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, ou seja.
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.
Uma situação em que pessoalmente encontro classes de fábrica separadas para fazer sentido é quando o objeto final que você está tentando criar se baseia em vários outros objetos. Por exemplo, no PHP: suponha que você tenha um House
objeto, que por sua vez tenha um Kitchen
e um LivingRoom
objeto, e o LivingRoom
objeto também tenha um TV
objeto dentro.
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 você House
falhar na criação, provavelmente passará algum tempo tentando isolar exatamente o que está falhando.
A alternativa é fazer o seguinte (injeção de dependência, se você gosta do termo sofisticado):
$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
falha falhar, há apenas um lugar para procurar, mas ter que usar essa parte toda vez que alguém quiser um novo House
está longe de ser conveniente. Entre nas 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 um House
é abstraído (pois você não precisa criar e configurar todas as dependências quando deseja apenas criar um House
) e ao mesmo tempo centralizado, o que facilita a manutenção. Há outras razões pelas quais usar Fábricas separadas pode ser benéfico (por exemplo, testabilidade), mas acho esse caso de uso específico para ilustrar melhor como as classes Factory podem ser úteis.
HouseFactory
classe?
create
método. Por exemplo, se você House
sempre tiver o mesmo tipo LivingRoom
, pode fazer sentido ter seus parâmetros codificados na classe factory em vez de passar como argumentos. Ou você pode fornecer um type
argumento para o seu HouseFactory::create
método se tiver alguns tipos de se houver LivingRoom
uma opção interna com os parâmetros codificados para cada tipo.
É importante diferenciar claramente a idéia por trás do uso de fábrica ou método de fábrica. Ambos destinam-se a abordar diferentes tipos de problemas de criação de objetos, mutuamente exclusivos.
Vamos ser específicos sobre o "método de fábrica":
A primeira coisa é que, quando você estiver desenvolvendo bibliotecas ou APIs que, por sua vez, serão usadas para desenvolvimento adicional de aplicativos, o método factory será uma das melhores opções para o padrão de criação. Razão para trás; Sabemos que, quando criar um objeto com a (s) funcionalidade (s) necessária (s), o tipo de objeto permanecerá indeciso ou será decidido pelos parâmetros dinâmicos passados .
Agora, o ponto é que aproximadamente o mesmo pode ser alcançado usando o próprio padrão de fábrica, mas uma grande desvantagem será introduzida no sistema se o padrão de fábrica for usado para o problema destacado acima, é que sua lógica de criar objetos diferentes (objetos de subclasses) seja específico a alguma condição de negócios; portanto, no futuro, quando você precisar estender a funcionalidade da sua biblioteca para outras plataformas (em mais tecnicamente, você precisará adicionar mais subclasses de interface básica ou abstrata, para que o factory retorne esses objetos, além do existente) 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á uma operação dispendiosa e não boa da perspectiva do projeto. Por outro lado, se "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();
}
}
É uma boa idéia usar métodos de fábrica dentro do objeto quando:
É uma boa idéia usar a classe abstrata de fábrica quando:
UML de
Produto: define uma interface dos objetos que o método Factory cria.
ConcreteProduct: Implementa a interface do produto
Criador: declara o método Factory
ConcreateCreator: implementa o método Factory para retornar uma instância de um ConcreteProduct
Declaração do problema: Crie um 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");
}
}
}
resultado:
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 Factory
classe implementando a FactoryMethod
.
Game
é a interface para todos os tipos de jogos. Ele define um método complexo:createGame()
Chess, Ludo, Checkers
são diferentes variantes de jogos, que fornecem implementação para createGame()
public Game getGame(String gameName)
está FactoryMethod
na IGameFactory
aula
GameFactory
pré-cria diferentes tipos de jogos no construtor. Implementa o IGameFactory
método de fábrica.
O nome do jogo é passado como argumento de linha de comando para NotStaticFactoryDemo
getGame
in GameFactory
aceita um nome de jogo e retorna o Game
objeto correspondente .
Fábrica:
Cria objetos sem expor a lógica da 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 em tempo de execução, mas apenas deseja obter uma classe que fará o trabalho.
getArea()
não é um método de fábrica em tudo .
É realmente uma questão de gosto. As classes de fábrica podem ser abstraídas / com interface, 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 têm um tipo definido, mas exigem um ponto de registro conhecido, semelhante a um serviço localizador, mas para localizar métodos de fábrica).
As classes de fábrica são úteis para quando o tipo de objeto que eles retornam possui 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 é associado ao seu tipo de concreto de retorno.
O WCF usa as 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 possui seu próprio DataServiceHostFactory e o ASP.NET possui seu ApplicationServicesHostFactory, pois seus serviços possuem construtores particulares.
Se você tiver apenas uma classe que está consumindo a fábrica, basta usar um método de fábrica nessa classe.
Considere um cenário em que você deve criar uma classe de Pedido e Cliente. Por questões de 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, surge um novo requisito: 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 sobrecarga de construtor que funcionará apenas para novas implementações. (Não aceitável). 2- Você altera as assinaturas de Order () e altera cada invocação. (Não é uma boa prática e muita dor).
Em vez disso, se você criou uma fábrica para a classe de pedidos, é necessário alterar apenas uma linha de código e pronto. Sugiro classe Factory para quase todas as associações agregadas. Espero que ajude.
se você deseja 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;
}
}
Qualquer classe que adie 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.
Mencionei em detalhes em outra resposta em https://stackoverflow.com/a/49110001/504133
Eu acho que vai depender do grau de acoplamento solto que você deseja trazer para o seu código.
O método de fábrica desacopla as coisas muito bem, mas a classe de fábrica não.
Em outras palavras, é mais fácil mudar as coisas se você usar o método de fábrica do que se usar uma fábrica simples (conhecida como classe de fábrica).
Veja este exemplo: https://connected2know.com/programming/java-factory-pattern/ . Agora, imagine que você quer trazer um novo animal. Na classe Factory, você precisa alterar a Factory, mas no método factory, não, você só precisa adicionar uma nova subclasse.
As classes de fábrica são mais pesadas, mas oferecem algumas vantagens. Nos casos em que você precisa construir seus objetos a partir de várias fontes de dados brutas, elas permitem que você encapsule apenas a lógica da construção (e talvez a agregação dos dados) em um único local. Lá, ele pode ser testado em abstrato sem se preocupar com a interface do objeto.
Eu achei esse um padrão útil, particularmente onde não consigo substituir e ORM inadequado e quero instanciar com eficiência muitos objetos de junções de tabela de DB ou procedimentos armazenados.
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 nomeados logicamente como Numbers
ou Shapes
. Estes são tipos genéricos que podem incluir números inteiros, flutuadores, dobules, compridos ou retângulos, círculos, triângulos, pentágonos no caso de formas.
O petter de fábrica usa 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 estrutura e todos os componentes discretos.
Aqui está um exemplo simples do conceito do Factory Pattern em PHP. 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 );
NumbersFactory::makeNumber( 'float', 12.5 );
me deixando apenas dizendo new Float(12.5);
se eu sei que preciso de um Float
? É isso que eu não entendo sobre as fábricas ... qual é o objetivo?