Programação Orientada a Objetos


Turma de 2003 - Professor Carlo Emmanoel

Abstract Factory

Ilan Chamovitz

Sandro de Azambuja

ilan@api.adm.br

sandro@soft.com.br

(Maio/2003)



Abstract Factory

Propósito
_ Prover uma interface para criar uma família de objetos relacionados ou
dependentes sem especificar suas classes concretas

Também chamado de

_ Kit

Resumo
_ Parece semelhante ao padrão Factory Method, mas
_ Em vez do cliente (que quer criar objetos sem saber as classes exatas) chamar um método de criação (Factory Method), ele de alguma forma possui um objeto (uma Abstract Factory) e usa este objeto para chamar os métodos de criação
_ Onde Factory Method quer que você seja diferente (via herança) para criar
objetos diferentes, o Abstract Factory quer que você tenha algo diferente
_ Se ele possuir uma referência a uma Abstract Factory diferente, toda a criação será diferente
_ O fato de todos os métodos de criação estarem na mesma subclasse de uma Abstract Factory permite satisfazer a restrição de criar apenas objetos
relacionados ou dependentes


Aplicação: Quando usar o padrão Abstract Factory?


_ Quando um sistema deve ser independente de como seus produtos são criados, compostos e representados
_ Quando um sistema deve ser configurado com uma entre várias famílias de produtos
_ Quando uma família de produtos relacionados foi projetada para uso conjunto e você deve implementar essa restrição
_ Quando você quer fornecer uma biblioteca de classes e quer revelar sua interface e não sua implementação
_ Não permitir, portanto, que objetos sejam diretamente criados com new



Estrutura genérica

Participantes
_ FactoryIF 
_ Define uma interface para as operações que criam objetos como produtos abstratos
_ FactoryAbstrata 
_ Possível classe abstrata para fatorar o código comum às FactoryConcretas
_ FactoryConcreta
_ Implementa as operações para criar objetos para produtos concretos
_ ProdutoXIF
_ Define uma interface para objetos de um tipo
_ ProdutoAbstrato 
_ Possível classe abstrata para fatorar o código comum aos ProdutosConcretos
_ ProdutoConcreto
_ Define um objeto produto a ser criado pela FactoryConcreta correspondente
_ Implementa a interface de ProdutoAbstrato
_ Cliente
_ Usa apenas interfaces definidas por FactoryIF e ProdutoXIF





ou 



Colaborações entre objetos

_ Normalmente uma única instância de uma classe FactoryConcreta é criada em tempo de execução
_ Essa FactoryConcreta cria objetos tendo uma implementação particular
_ Para criar produtos diferentes, clientes devem usar uma FactoryConcreta
diferente 
_ FactoryAbstrata depende de suas subclasses FactoryConcreta para criar objetos de produtos

Exemplo no BlueJ:



Consequências do uso do padrão Abstract Factory

_ O padrão isola classes concretas
_ Uma factory encapsula a responsabilidade e o processo de criação de objetos de produtos
_ Isola clientes das classes de implementação
_ O cliente manipula instâncias através de suas interfaces abstratas
_ Facilita o câmbio de famílias de produtos
_ A classe da FactoryConcreta só aparece em um lugar: onde ela é instanciada 
_ Uma mudança numa única linha de código pode ser suficiente para mudar a FactoryConcreta que a aplicação usa 
_ A família inteira de produtos muda de uma vez
_ Promove a consistência entre produtos
_ Produtos de uma determinada família devem funcionar conjuntamente e não misturados com produtos de outra família
_ O padrão permite implementar esta restrição com facilidade
_ Do lado negativo: dar suporte a novos tipos de produtos é difícil
_ O motivo é que a FactoryAbstrata fixa o conjunto de produtos que podem ser criados
_ Dar suporte a mais produtos força a extensão da interface da factory o que
envolve mudanças na FactoryAbstrata e em todas suas subclasses FactoryConcreta

Considerações de implementação

_ Factory como padrão Singleton
_ Uma aplicação normalmente só precisa de uma única instância de uma
FactoryConcreta por família de produtos
_ O padrão Singleton ajuda a controlar a instância única
_ Criação dos produtos
_ A FactoryIF apenas define a interface de criação
_ Quem cria os objetos são as FactoryConcreta
_ Tais subclasses são freqüentemente implementadas usando o padrão
Factory Method
_ Uma FactoryConcreta faz override do Factory Method de cada produto


Exemplo de código: criação de labirintos

Modelo:


O padrão AbstractFactory sendo aplicado na criação de labirintos:


1. É definida uma abstract factory chamada MazeFactory a qual pode criar os componentes dos labirintos.
a. Programas que constróem labirintos utilizam um MazeFactory como um argumento portanto o programador pode especificar suas classes de objetos para construir um labirinto (maze).
b. A MazeFactory é apenas uma coleção de factory methods
c. A MazeFactory utiliza a AbstractFactory e a ConcreteFactory
2. Reescrever a CreateMaze para utilizar os factory methods da MazeFactory
a. CreateMAze utiliza a MazeFactory como um parâmetro o qual permite ao cliente escolher qualquer maze factory.
3. Redefinir alguns ou todos as operações para especificar variações nas famílias de produtos através da utilização da MazeFactory como subclasse.
a. Uma BombedMazeFactory pode redefinir as operações MakeRoom e a MakeWall para que retornem "bombed varieties".
b. Uma EnchantedMazeFactory pode redefinir as operações MakeRoom e a MakeWall para que retornem "enchanted varieties".
4. Para construir um simples labirinto que contenha bombas, nós simplesmente chamamos CreateMaze através da BombedMazeFactory.


Exemplo do código (parte do modelo) 


_ Todos os Factory Methods do padrão anterior saem da classe Jogo e entram na factory abstrata FactoryDeLabirinto
_ Também possuem um default, o que significa que FactoryDeLabirinto também é uma factory concreta
_ Também usamos o design pattern Singleton aqui

public interface FactoryDeLabirintoIF {
public LabirintoIF criaLabirinto();
public SalaIF criaSala(int númeroDaSala);
public ParedeIF criaParede() {
public PortaIF criaPorta(SalaIF sala1, SalaIF sala2) {
}
public class FactoryDeLabirinto implements FactoryDeLabirintoIF {
private static FactoryDeLabirintoIF instânciaÚnica = null;
private FactoryDeLabirinto() {}
public static FactoryDeLabirintoIF getInstance(String tipo) {
if(instânciaÚnica == null) {
if(tipo.equals("perigoso")) {
instânciaÚnica = new FactoryDeLabirintoPerigoso();
} else if(tipo.equals("encantado")) {
instânciaÚnica = new FactoryDeLabirintoEncantado();
} else {
instânciaÚnica = new FactoryDeLabirinto();
}
}
return instânciaÚnica;
}
// Factory Methods
// Tem default para as Factory Methods
public LabirintoIF criaLabirinto() {
return new Labirinto();
}
public SalaIF criaSala(int númeroDaSala) {
return new Sala(númeroDaSala);
}
public ParedeIF criaParede() {
return new Parede();
}
public PortaIF criaPorta(SalaIF sala1, SalaIF sala2) {
return new Porta(sala1, sala2);
}
}
_ A nova versão de criaLabirinto recebe um FactoryDeLabirintoIF como parâmetro e cria um labirinto
public class Jogo implements JogoIF {
// Observe que essa função não tem new: ela usa uma Abstract Factory
// Esta é a *única* diferença com relação à versão original
// Observe como criaUmLabirinto acessa a factory (através de um
Singleton)
public LabirintoIF criaUmLabirinto(FactoryDeLabirintoIF factory) {
LabirintoIF umLabirinto = factory.criaLabirinto();
SalaIF sala1 = factory.criaSala(1);
SalaIF sala2 = factory.criaSala(2);
PortaIF aPorta = factory.criaPorta(sala1, sala2);
umLabirinto.adicionaSala(sala1);
umLabirinto.adicionaSala(sala2);
sala1.setVizinho(NORTE, factory.criaParede());
sala1.setVizinho(LESTE, aPorta);
sala1.setVizinho(SUL, factory.criaParede());
sala1.setVizinho(OESTE, factory.criaParede());
Abstract Factory Página 10 de 14
file://C:\Documents and Settings\jacques\Meus documentos\Cursos\Hp\map\html\pat\abstractfactory.htm 25/2/2002
sala2.setVizinho(NORTE, factory.criaParede());
sala2.setVizinho(LESTE, factory.criaParede());
sala2.setVizinho(SUL, factory.criaParede());
sala2.setVizinho(OESTE, aPorta);
return umLabirinto;
}
}

_ Para criamos um labirinto encantado, criamos uma factory concreta como subclasse de FactoryDeLabirinto

public class FactoryDeLabirintoEncantado extends FactoryDeLabirinto {
public SalaIF criaSala(int númeroDaSala) {
return new salaEncantada(númeroDaSala, jogaEncantamento());
}
public PortaIF criaPorta(SalaIF sala1, SalaIF sala2) {
return new portaPrecisandoDeEncantamento(sala1, sala2);
}
protected EncantamentoIF jogaEncantamento() {
...
}
}
_ Para criarmos um labirinto perigoso, criamos uma outra factory concreta como
subclasse de FactoryDeLabirinto

public class FactoryDeLabirintoPerigoso extends FactoryDeLabirinto {
public ParedeIF criaParede() {
return new paredeDestruível();
}
public SalaIF criaSala(int númeroDaSala) {
return new salaComBomba(númeroDaSala);
}
}
_ Finalmente, podemos jogar:
JogoIF umJogo = new Jogo();
FactoryDeLabirinto factory = FactoryDeLabirinto.getInstance("perigoso");
jogo.criaLabirinto(factory);


_ Poderíamos jogar um jogo encantado com uma mudança muito simples ao código
acima
_ O diagrama de objetos tem um objeto a mais comparado com a versão original

Discussão geral de padrões de criação

_ Ajudam a deixar o sistema independente de como seus objetos são criados, compostos e representados
_ São dois tipos:
_ Padrões de criação via classes
_ Usam herança para variar a classe que é instanciada
_ Exemplo: Factory Method
_ Padrões de criação via objetos
_ Delegam a instanciação para outro objeto
_ Exemplo: Abstract Factory
_ Composição é usada mais que herança para estender funcionalidade e padrões de criação ajudam a lidar com a complexidade de criar comportamentos
_ Em vez de codificar um comportamento estaticamente, definimos pequenos comportamentos padrão e usamos composição para definir comportamentos mais complexos
_ Isso significa que instanciar um objeto com um comportamento particular
requer mais do que simplesmente instanciar uma classe
_ Eles escondem como instâncias das classes concretas são criadas e juntadas para gerar "comportamentos" (que podem envolver vários objetos compostos)
_ Os padrões mostrados aqui mostram como encapsular as coisas de forma a simplificar o problema de instanciação
_ Os padrões de criação discutem temas recorrentes:
_ Eles encapsulam o conhecimento das classes concretas que são instanciadas
_ Lembre que preferimos nos "amarrar" a interfaces (via interface ou classes
abstratas) do que a classes concretas
_ Isso promove a flexibilidade de mudança (das classes concretas que são
instanciadas)



Referências web:

1. jamesthornton.com/eckel/TIPatterns/html/Contents.html 
2. developer.java.sun.com/developer/technicalArticles/Programming/singletons/ 
3. www.patterndepot.com/put/8/JavaPatterns?.htm
4. www.research.umbc.edu/~tarr/dp/fall00/cs491.html 
5. www.javacoder.net/patterns.jsp 
6. home.earthlink.net/~huston2/dp/patterns.html 
7. www.inf.furb.br/seminco/2002/artigos/Oss-seminco2002-33.pdf
8. www.agcs.com/supportv2/techpapers/patterns/papers/tutnotes/index.htm 







Complemento: 
UML Genérica - Abstract Factory