Saltar para o conteúdo

Factory Method

Origem: Wikipédia, a enciclopédia livre.

Factory Method ou Construtor virtual, na ciência da computação, é um padrão de projeto de software (design pattern, em inglês) que permite às classes delegar para subclasses decidirem, isso é feito através da criação de objetos que chamam o método fabrica especificado numa interface e implementado por um classe filha ou implementado numa classe abstrata e opcionalmente sobrescrito por classes derivadas.

Diagrama UML da estrutura do padrão Factory Method

O padrão Factory Method, da forma como foi descrito no livro Design Patterns: Elements of Reusable Object-Oriented Software, contém os seguintes elementos:

  • Creator(Criador abstrato) — declara o factory method (método de fabricação) que retorna o objeto da classe Product (produto). Este elemento também pode definir uma implementação básica que retorna um objeto de uma classe ConcreteProduct (produto concreto) básica;
  • ConcreteCreator(Criador concreto) — sobrescreve o factory method e retorna um objeto da classe ConcreteProduct;
  • Product(Produto abstrato) — define uma interface para os objectos criados pelo factory method;
  • ConcreteProduct(Produto concreto) — uma implementação para a interface Product.

Criar um objeto geralmente requer processos complexos não apropriados para incluir dentro da composição do objeto. A criação do objeto talvez necessite de uma duplicação de código significativa, talvez necessite informações não acessíveis para a composição do objeto, talvez não providencie um grau de abstração suficiente, ou então não faça parte da composição das preocupações do objeto. O padrão de design método fabrica maneja/trata esses problemas definindo um método separado para criação dos objetos, no qual as subclasses possam sobrescrever para especificar o "tipo derivado" do produto que vai ser criado.

Este padrão é muito utilizado em frameworks para definir e manter relacionamentos entre objetos. O framework Spring, dependendo da configuração, pode utilizar um Factory Method para criar os seus beans.[1]

Este padrão pode ser utilizado na construção de um framework que suporta aplicações que apresentam múltiplos documentos ao usuário. Normalmente este tipo de aplicação manipula um número variável de formatos de documento e, por isso, este framework deve ser flexível o bastante para suportar qualquer formato. Uma solução para este problema poderia disponibilizar, no framework, o código para alguns dos formatos mais utilizados. Mas, na prática, esta solução seria uma implementação pouco flexível, e até mesmo incompleta, já que é custoso implementar os mais variados formatos. O padrão Factory Method propõe uma solução que deixa para o cliente (a implementação da aplicação) a tarefa de suportar os formatos necessários e para o framework o papel de definição de uma abstração que oferece uma interface única para criação de documentos.

Este framework seria baseado em duas classes abstratas, que representam a Aplicação e o Documento. O cliente do framework fornece um par de classes concretas, uma aplicação e o respectivo documento, para cada um dos formatos de Documento suportados pela Aplicação. Se for necessário apresentar um documento que suporte desenho, por exemplo, o cliente deve disponibilizar as classes AplicacaoDesenho e DocumentoDesenho (supondo que o sufixo "Desenho" indique classes que suportam esta funcionalidade).

O objetivo do Factory Method está em diversas classes que implementam a mesma operação, retornarem o mesmo tipo abstrato, mas internamente instanciam diferentes classes que o implementam. Com o Factory Method o criador do objeto faz uma escolha de qual classe instanciar para o cliente. Para ser um Factory Method o método precisa retornar uma interface ou uma classe abstrata e, dependendo das necessidades do cliente, criar um objeto determinado como retorno. Um exemplo clássico do Factory Method são os iteradores tanto em Java como em .NET.

Consequências

[editar | editar código-fonte]
  • Positivas: Baixo acoplamento, maior flexibilidade e elimina a necessidade de acoplar classes específicas para aplicação em nível de código.
  • Negativas: Alto número de classes, podendo sobrecarregar o sistema.
  • Quando a classe não antecipa a classe do objeto que quer criar.
  • Uma classe quer suas subclasses para especificar os objetos que cria.
  • Quando você não quer que o usuário tenha que saber de cada subclasse.
  • Encapsular a criação de objetos.

Factory Method é utilizado para criar objetos assim como o Template Method é usado para criar algoritmos. Uma superclasse específica todos os padrões e comportamentos genéricos, e depois delega os detalhes da criação para as subclasses que são fornecidas pelo cliente. O Factory Method faz uma design mais customizável e somente um pouco mais complicado. Outros design patterns requerem novas classes, enquanto que o Factory Method precisa somente de uma no operação. As pessoas regularmente utilizam o Factory Method como o padrão de criar objetos; mas não é necessário se:

  • Uma classe que é instanciada nunca mude.
  • A instanciação toma lugar em uma operação que subclasses podem facilmente sobrescrever (como a inicialização de uma operação).

O Factory Method é similar ao Abstract Factory (mas sem a ênfase as famílias) e também os métodos fábricas são especificados rotineiramente por uma arquitetura framework, e depois implementado pelo usuário do framework.

Dicas/Considerações/Boas Práticas

[editar | editar código-fonte]
  • Classes Abstract Factory são usualmente implementadas com Factory Methods, mas elas podem ser implementadas utilizando Prototype.
  • Factory Methods são geralmente chamados dentro de Template Methods.
  • O operador new é considerado prejudicial. Existe diferença entre requisitar um objeto e criar um. O operador new sempre cria um objeto, e falha ao encapsular o objeto criação. Uma Factory Method aplica essa encapsulação, e permite um objeto ser requisitado sem acoplamento inextricável ao ato de criação.
  • A vantagem do Factory Method é que este padrão pode retornar uma mesma instância múltiplas vezes, ou pode retornar a subclasse invés de um objeto daquele tipo exato.
  • Factory Methods: criação através de herança. Prototype :criação através de delegação.
  • Frequentemente , designs começam pelo Factory Method (menos complicado, mais customizável, subclasses proliferam) e evoluem para Abstract Fatory,Prototype ou Builder (mais flexível, mais complexo) enquanto o desenvolvedor descobre onde a flexibilidade é mais necessária.
  • Prototype não requer subclasses, mas precisa de uma operação de inicialização. Factory Method requer subclasses, mas não requer inicialização.
  • Algumas pessoas defendem que, como uma questão de language design, absolutamente todos os construtores devem ser private ou protected. Não é problema de ninguém se uma classe manufatura um objeto novo ou recicla um velho.

Neste exemplo, uma aplicação, que é construída através de um framework baseado no padrão Factory Method, suporta a criação de documentos do tipo MeuDocumento. O framework é constituído pelas classes abstratas Aplicacao e Documento. A aplicação disponibiliza as classes concretas MinhaAplicacao e MeuDocumento. A classe MinhaAplicacao é uma implementação da abstração definida pela classe Aplicacao.

Exemplo de Diagrama em UML para o Padrão Factory Method.

A chave deste padrão está na declaração do método abstrato criaDocumento, da classe Aplicacao, e na sua utilização pelo método novoDocumento. Este arranjo permite que o método novoDocumento crie documentos sem conhecer os detalhes de implementação, existentes em cada tipo de documento suportado pela aplicação. Isto permite que a implementação do método criaDocumento (neste exemplo situada na classe MinhaAplicacao) varie livremente, para atender os diversos formatos possivelmente suportados, sem que seja necessário modificar o código das classes abstratas.

Código em Java

[editar | editar código-fonte]

Este código, escrito na linguagem Java, demonstra a implementação do diagrama mostrado acima.

/**
* Abstração de uma Aplicação capaz de manipular
* documentos.
*/
 abstract class Aplicacao {

      protected Documento doc;

      /**
      * Abstração do Factory Method
      */
       abstract Documento criaDocumento();

       void novoDocumento() {
           this.doc = this.criaDocumento();
       }

       void abrirDocumento() {
           this.doc.abrir();
       }
  }

/**
* Abstração de um Documento.
*/
  abstract class Documento {

      void abrir() {
          System.out.println("Documento:Abrir documento!");
      }

      void fechar() {
          System.out.println("Documento:Fechar documento!");
      }

      void gravar() {
          System.out.println("Documento:Gravar documento!");
      }
  }

/**
* Esta classe concreta contém a implementação
* de uma aplicação capaz de manipular documentos
* do tipo MeuDocumento.
*/
  class MinhaAplicacao extends Aplicacao {

 	/**
 	 * Uma implementação do Factory Method. Este método é
 	 * especializado na criação de documentos do tipo MeuDocumento
 	 */
 	Documento criaDocumento() {
            return new MeuDocumento();
 	}
  }

/**
* Esta classe concreta contém a implementação
* de um tipo de documento específico.
*/
  class MeuDocumento extends Documento {

  }

Código em VB.NET

[editar | editar código-fonte]

O mesmo exemplo acima, em linguagem Visual Basic .NET.

Public MustInherit Class Aplicacao

    Private doc As Documento

    MustOverride Function criaDocumento() As Documento

    Sub novoDocumento()
        Me.doc = Me.criaDocumento
    End Sub

    Sub abrirDocumento()
        Me.doc.abrir()
    End Sub

End Class

Public MustInherit Class Documento

    Sub abrir()
        Console.WriteLine("Documento:Abrir documento!")
    End Sub

    Sub fechar()
        Console.WriteLine("Documento:Fechar documento!")
    End Sub

    Sub gravar()
        Console.WriteLine("Documento:Gravar documento!")
    End Sub

End Class

Public Class MinhaAplicacao
    Inherits Aplicacao

    Public Overrides Function criaDocumento() As Documento

        Return New MeuDocumento

    End Function
End Class

Public Class MeuDocumento
    Inherits Documento

End Class

Código em C#

[editar | editar código-fonte]

O mesmo exemplo acima, em linguagem C#.

public abstract class Aplicacao
{
    private Documento doc;

    Documento criaDocumento();

    void novoDocumento()
    {
        this.doc = this.criaDocumento();
    }
		

    void abrirDocumento()
    {
        this.doc.abrir();
    }

}

public abstract class Documento
{
    void abrir()
    {
        Console.WriteLine("Documento:Abrir documento!");
    }

    void fechar()
    {
        Console.WriteLine("Documento:Fechar documento!");
    }

    void gravar()
    {
        Console.WriteLine("Documento:Gravar documento!");
    }
}

public class MinhaAplicacao : Aplicacao
{
    public Documento criaDocumento()
    {
        return new MeuDocumento();
    }
}

public class MeuDocumento : Documento
{

}

Neste próximo exemplo, é demonstrado a utilização do padrão Factory Method para a criação de diferentes refrigerantes. Veja a imagem a seguir:

A imagem abaixo mostra como funciona o padrão Factory Method em forma de imagem:

FactoryMethodBRPT

Observação: as classes RefrigeranteCola e RefrigeranteLaranja estendem a classe abstrata Refrigerante.

Segue o código do diagrama acima (em Java):

public abstract class Refrigerante {

	private String nome;

	public String getName(){
		return nome;
	}

	public void setName(String nome){
		this.nome = nome;
	}
	
	public void abrir(){
		System.out.println("Você abriu uma lata de "+getName());
	}

}





public class RefrigeranteCola extends Refrigerante{
	
	public RefrigeranteCola(){
		setName("Koka Kola");
	}
	
}





public class RefrigeranteLaranja extends Refrigerante{
	
	public RefrigeranteLaranja(){
		setName("Phanta Laranja");
	}
	
}




public class RefrigeranteFactory {
	
	public Refrigerante fazerRefrigerante(String tipo){		
		Refrigerante refri = null;	

		if(tipo.equals("K")){
			return new RefrigeranteCola();
		}else if(tipo.equals("P")){
			return new RefrigeranteLaranja();
		}else return null;
	}
	
}



import java.util.Scanner;
public class Cliente{
	public static void main (String[] args){
		Scanner ler = new Scanner(System.in);
		
		RefrigeranteFactory rf = new RefrigeranteFactory();
		Refrigerante refrigerante = null;

		System.out.println("Qual refrigerante você quer? (K / P)");
		if(ler.hasNextLine()){
			String tipo = ler.nextLine();
			refrigerante = rf.fazerRefrigerante(tipo);
		}

		if(refrigerante != null){
			executar(refrigerante);
		} else System.out.println("Digite K ou P...");
	}

	public static void executar(Refrigerante refri){
		refri.abrir();
	}	

}

A imagem abaixo mostra como funciona o padrão Factory Method em forma de imagem:

ResumoFactoryMethodPTBR
                                  Resumo: O Factory Method permite que você crie objetos sem especificar a classe exata do objeto que vai ser criado.


Outros Exemplos

[editar | editar código-fonte]

Os exemplos a seguir mostram a codificação do padrão Factory Method em outras linguagens. Obs: os exemplos são aleatórios retirados da internet.

Código em Python

[editar | editar código-fonte]
class Car(object):

    def factory(type):
        if type == "Racecar":
            return Racecar()
        if type == "Van":
            return Van()
        assert 0, "Bad car creation: " + type

    factory = staticmethod(factory)

class Racecar(Car):
    def drive(self): print("Racecar driving.")

class Van(Car):
    def drive(self): print("Van driving.")

//Criar objeto usando factory.
obj = Car.factory("Racecar")
obj.drive()

Código em Delphi

[editar | editar código-fonte]
TRedSpeedButton = class(TSpeedButton)
  public
    constructor Create(AOwner: TComponent); override;
  end;

constructor TRedSpeedButton.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  Font.Color := clRed;
end;

function TORedFactory.CreateSpeedButton(AOwner: TComponent): TSpeedButton;
begin
  Result := TRedSpeedButton.Create(AOwner);
end;

Código em PHP

[editar | editar código-fonte]
<?php

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

class OReillyFactoryMethod extends AbstractFactoryMethod {
    private $context = "OReilly";
    function makePHPBook($param) {
    $book = NULL;
        switch ($param) {
            case "us":
                $book = new OReillyPHPBook;
            break;
            case "other":
                $book = new SamsPHPBook;
            break;
            default:
                $book = new OReillyPHPBook;
            break;
        }
    return $book;
    }
}

class SamsFactoryMethod extends AbstractFactoryMethod {
    private $context = "Sams";
    function makePHPBook($param) {
        $book = NULL;
        switch ($param) {
            case "us":
                $book = new SamsPHPBook;
            break;
            case "other":
                $book = new OReillyPHPBook;
            break;
            case "otherother":
                $book = new VisualQuickstartPHPBook;
            break;
            default:
                $book = new SamsPHPBook;
            break;
        }
        return $book;
    }
}

abstract class AbstractBook {
    abstract function getAuthor();
    abstract function getTitle();
}

abstract class AbstractPHPBook {
    private $subject = "PHP";
}

class OReillyPHPBook extends AbstractPHPBook {
    private $author;
    private $title;
    private static $oddOrEven = 'odd';
    function __construct() {
        //alternate between 2 books
        if ('odd' == self::$oddOrEven) {
            $this->author = 'Rasmus Lerdorf and Kevin Tatroe';
            $this->title  = 'Programming PHP';
            self::$oddOrEven = 'even';
        } else {
            $this->author = 'David Sklar and Adam Trachtenberg';
            $this->title  = 'PHP Cookbook';
            self::$oddOrEven = 'odd';
        }
    }
    function getAuthor() {return $this->author;}
    function getTitle() {return $this->title;}
}

class SamsPHPBook extends AbstractPHPBook {
    private $author;
    private $title;
    function __construct() {
        //alternate randomly between 2 books
        mt_srand((double)microtime()*10000000);
        $rand_num = mt_rand(0,1);

        if (1 > $rand_num) {
            $this->author = 'George Schlossnagle';
            $this->title  = 'Advanced PHP Programming';
        } else {
            $this->author = 'Christian Wenz';
            $this->title  = 'PHP Phrasebook';
        }
    }
    function getAuthor() {return $this->author;}
    function getTitle() {return $this->title;}
}

class VisualQuickstartPHPBook extends AbstractPHPBook {
    private $author;
    private $title;
    function __construct() {
      $this->author = 'Larry Ullman';
      $this->title  = 'PHP for the World Wide Web';
    }
    function getAuthor() {return $this->author;}
    function getTitle() {return $this->title;}
}

  writeln('START TESTING FACTORY METHOD PATTERN');
  writeln('');

  writeln('testing OReillyFactoryMethod');
  $factoryMethodInstance = new OReillyFactoryMethod;
  testFactoryMethod($factoryMethodInstance);
  writeln('');

  writeln('testing SamsFactoryMethod');
  $factoryMethodInstance = new SamsFactoryMethod;
  testFactoryMethod($factoryMethodInstance);
  writeln('');

  writeln('END TESTING FACTORY METHOD PATTERN');
  writeln('');

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

    $phpUs = $factoryMethodInstance->makePHPBook("other");
    writeln('other php Author: '.$phpUs->getAuthor());
    writeln('other php Title: '.$phpUs->getTitle());

    $phpUs = $factoryMethodInstance->makePHPBook("otherother");
    writeln('otherother php Author: '.$phpUs->getAuthor());
    writeln('otherother php Title: '.$phpUs->getTitle());
  }

  function writeln($line_in) {
    echo $line_in."<br/>";
  }
?>

Padrões relacionados

[editar | editar código-fonte]

Referências

  1. Rod Johnson, Juergen Hoeller, Alef Arendsen, Colin Sampaleanu, Rob Harrop, Thomas Risberg, Darren Davison, Dmitriy Kopylenko, Mark Pollack, Thierry Templier, Erwin Vervaet, Portia Tung, Ben Hale, Adrian Colyer, John Lewis, Costin Leau, Rick Evans. «The Spring Framework - Reference Documentation» (em inglês). Consultado em 16 de maio de 2007