Submarino.com.br

Factory

Objetivo

Permitir que a criação de um objeto seja controlada por outro.

Propósito

Factory (Fábrica) é um padrão de projeto que visa encapsular a criação de um objeto de certa classe em um outro objeto de outra classe [1].

Factory é provavelmente um dos padrões que mais influencia a escrita de programas orientados a objetos que se utilizam do Princípio da Inversão de Controle quando chega a hora de instanciar objetos. O Principio de Inversão de Controle troca o controle do objeto utilizador para o objeto utilizado. No caso da criação de objeto isto significa que se um objeto A quer uma instância do objeto B a diretiva new não pode ser usada. A opção então é delegar a um objeto C que se encarrega de criar o objeto B de alguma forma.

Algumas razões existem para que não se queira utilizar new diretamente. A primeira, e mais óbvia, é não saber que classe de objeto instanciar. Isso é comum numa aplicação bem desenhada onde variáveis são estruturadas com base em interfaces. Assim, vários tipos de objetos diferentes podem ser associadas a essa variável. Porque várias opções existem para o tipo de objeto que será atribuído à variável é útil delegar a escolha para um outro objeto: a fábrica.

Uma outra razão é a necessidade de inicializar o objeto instâncias antes de ser atribuído à variável. Importante notar que esta inicialização não depende do ambiente onde o objeto será utilizado, mas apenas da estrutura do próprio objeto. Quando o ambiente em que o objeto será usado necessita configurar o objeto sem saber qual ele é realmente o padrão Builder é utilizado. O padrão Builder prepara a construção e inicialização do objeto antes de o devolver. O padrão Factory tem total autonomia para fazer essas operações sem intervenção de mais ninguém.

Implementação

A implementação de Factory pode ser tão simples como aplicar new até ler um arquivo de configuração de um arquivo e criar um objeto de proxy dinâmico através dessas configurações.


public class TaxCalculatorFactory  {

 private Locale locale;
 
 public Factory (Locale locale){
	this.locale = locale;
 }

 public TaxCalculator newInstance (){
  return new LocaleTaxCalculator (locale) ;
 }
 
}

Código 1:

Aqui a implementação é bastante simples instanciando um objeto da classe LocaleTaxCalculator com new, mas na realidade não há limite para o que um Factory pode fazer. Em geral a fábrica tem na assinatura o retorno de uma interface e não de uma classe. É possível que o Factory devolva uma classe, mas é mais comum ver um interface sendo retornada porque normalmente o principio de Design para Interfaces é seguido.

É importante notar que o método que cria o objeto é de instância. Ou seja, é preciso instanciar a classe de Factory para invocar o método. Caso contrário estaríamos na presença de Factory Method ou Service Locator. O objetivo de ter um objeto ? e não um método ? para instanciar outro objeto é promover polimorfismo da própria fábrica. Ou seja, a fabrica, ela mesma, por ser instanciada por outra fábrica ou podem existir várias implementações da mesma fábrica, conforme o caso.

A fábrica contém normalmente um conjunto de parâmetros que permitem configurar qual objeto criar ou como inicializar o objeto criado. Desta forma, diferentes valores para este conjunto de parâmetros produzirá diferentes objetos o que é onde o padrão Factory se torna realmente mais útil e flexível que a simples invocação direta de new.

Fábricas e os Princípios Básicos do Design

Factory é o padrão que se relaciona a muitos princípios básico de programação orientada a objetos como sejam o Princípio de Separação de Responsabilidade (Separation of Control - SoC) e Princípio da Inversão de Controle (Inversion of Control - IoC).

Ao remover da classe que usa o objeto a responsabilidade de o inicializar tornamos essa classe menos acoplada ao resto do sistema. A classe pode agora usar o objeto sem se preocupar com criá-lo e inicializá-lo. Por outro lado, como o objeto vai ser criado apenas em um único lugar ele não precisa prover muitas formas de inicialização, diminuindo a responsabilidade de tentar saber como as outras classes vão trabalhar com ele. Em outras palavras: usar Factory torna seu código menos acoplado e portanto mais flexível a mudanças.

Com o advento de sistemas complexos o uso de padrões como Factory e seu primo Service Locator ficou claro que o acoplamento ainda se mantinha. É verdade que encapsulamos a instanciação do objeto que nos interessa mas temos agora que instanciar a fábrica. Ou, se não sabemos qual é, temos que instanciar a fábrica da fábrica? A única forma de resolver este problema é não obrigar a classe que usa o objeto a saber como obter esse objeto. Simplesmente se permite que a variável seja preenchida do exterior (um método modificador). Desta forma, é o ambiente onde a classe está sendo usada que irá decidir qual o objeto concreto será usado e de onde será obtido. Este é o conceito por detrás do Princípio da Inversão de Controle.

Aplicando o Princípio da Inversão de Controle para resolução do problema especifico de descobrir qual instância usar chegamos no conceito de Injeção de Dependência (DI). A idéia é simples. De alguma forma você declara qual objeto será usado por uma classe em particular. Um sistema especial ? chamado ambiente de injeção (ou injection container em inglês) ? irá ler essa informação, inicializar o objeto certo e anexá-lo ao objeto que o irá usar. Esta técnica remove o uso explicito de Factory no seu código e introduz um mecanismo novo. Frameworks como o Spring [2] e o Guice [3] fornecem esta capacidade.

Eliminamos assim, o problema de instanciar suas classes Factory dentro do seu código, mas não deixou de necessitar delas. O ambiente de injeção apenas transporta a responsabilidade da inicialização para fora do seu código, contudo ele não elimina essa necessidade. Afinal, os objetos têm que ser criados. O Injection Container é na realidade um Service Locator de Factory capaz de entender qual factory é usada para qual classe.

Mesmo com o uso de ambientes de injeção pode ser necessário criar fábricas especificas para instanciar seus objetos. O que o ambiente pode fazer por si é liberá-lo de fazer isso no casos simples em que a fabrica se limitaria a invocar new. Contudo na prática é muitas vezes necessário escrever sua própria fábrica de objetos devido à complexidade da inicialização ou aos cuidados necessários. A única forma de encapsular a instanciação de objetos é usando o padrão Factory, mesmo quando você utilizar um ambiente de injeção.

Padrões relacionados

O padrão Factory nasce da aplicação de polimorfismo ao padrão Factory Method e é comum ser utilizado em combinação com outros padrões de projeto já que em muitos casos é necessário encapsular a criação de algum objeto.

O padrão Factory está também associado a uma filosofia de programação orientada pelo uso de Injeção de Dependência, já que o ambiente de injeção é formalmente implementado como um Factory. A diferença é que ele produzirá qualquer tipo de objeto em particular produzirá outros Factory e/ou utilizará Factory para criar outros objetos.

Um outro padrão relacionado a Factory é o padrão Service Locator. Embora possamos pensar em Service Locator como uma especialização de Factory Method existe a necessidade de passar parâmetros para o objeto para que ele faça a sua função. Em particular podemos ter diferentes implementações que procuram os objetos de forma diferente ou em diferentes fontes.

Referências

[1] Design Patterns: Elements of Reusable Object-Oriented Software

Livro:Design Patterns: Elements of Reusable Object-Oriented Software

[2] Spring Framework

URL: Spring Framework http://www.springframework.org/

[3] Google Guice

URL: Google Guice http://code.google.com/p/google-guice/