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) ; } }
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: |
[3] |
Google Guice
URL: |