DEFINIR UMA INTERFACE PARA CRIAR OBJETOS DE FORMA A DEIXAR SUBCLASSES DECIDIREM QUAL CLASSE INSTANCIAR
FACTORY METHOD DEIXA QUE SUBCLASSES FAÇAM A INSTANCIAÇÃO
TAMBÉM CHAMADO DE "CONSTRUTOR VIRTUAL"
A IDÉIA É SIMPLES: EM VEZ DE UM CLIENTE QUE PRECISA DE UM OBJETO CHAMAR new E ASSIM ESPECIFICAR A CLASSE CONCRETA QUE ELE INSTANCIA, O CLIENTE CHAMA UM MÉTODO ABSTRATO (FACTORY METHOD) ESPECIFICADO EM ALGUMA CLASSE ABSTRATA (OU INTERFACE) E A SUBCLASSE CONCRETA VAI DECIDIR QUE TIPO EXATO DE OBJETO CRIAR E RETORNAR
MUDAR A SUBCLASSE CONCRETA QUE CRIA O OBJETO PERMITE MUDAR A CLASSE DO OBJETO CRIADO SEM QUE CLIENTE SAIBA
PERTMITE ESTENDER A FUNCIONALIDADE ATRAVÉS DA CONSTRUÇÃO DE SUBCLASSES SEM AFETAR OS CLIENTES
RESUMINDO:
"CRIE OBJETOS NUMA OPERAÇÃO SEPARADA DE FORMA QUE SUBCLASSES POSSAM FAZER OVERRIDE DA FORMA DE CRIAÇÃO"
QUANDO USAR O PADRÃO FACTORY METHOD?
QUANDO UMA CLASSE (O CRIADOR) NÃO PODE ANTECIPAR A CLASSE DOS OBJETOS QUE DEVE CRIAR
UMA CLASSE QUER QUE SUAS SUBCLASSES ESPECIFIQUEM OS OBJETOS CRIADOS
CLASSES DELEGAM RESPONSABILIDADE PARA UMA ENTRE VÁRIAS SUBCLASSES DE APOIO E QUEREMOS LOCALIZAR NUM PONTO ÚNICO A CONHECIMENTO DE QUAL SUBCLASSE ESTÁ SENDO USADA
ESTRUTURA GENÉRICA
EXERCÍCIO PARA CASA: REDESENHE O DIAGRAMA USANDO INTERFACES
PARTICIPANTES
Produto
DEFINE A INTERFACE DOS OBJETOS CRIADOS PELO FACTORY METHOD
ProdutoConcreto
IMPLEMENTA A INTERFACE Produto
Criador
DECLARA O FACTORY METHOD QUE RETORNA UM OBJETO DO TIPO Produto
ÀS VEZES, O Criador NÃO É UMA CLASSE ABSTRATA E TEM UMA IMPLEMENTAÇÃO DEFAULT PARA O FACTORY METHOD PARA RETORNAR UM OBJETO COM TIPO ProdutoConcreto DEFAULT
PODE CHAMAR O FACTORY METHOD PARA CRIAR UM PRODUTO DO TIPO Produto
CriadorConcreto
FAZ OVERRIDE DO FACTORY METHOD PARA RETORNAR UMA INSTÂNCIA DE ProdutoConcreto
COLABORAÇÕES
Criador DEPENDE DE SUAS SUBCLASSES PARA DEFINIR O FACTORY METHOD PARA QUE ELE RETORNE UMA INSTÂNCIA DO ProdutoConcreto APROPRIADO
CONSEQUÊNCIAS DO USO DO PADRÃO FACTORY METHOD
FACTORY METHODS ELIMINAM A NECESSIDADE DE COLOCAR CLASSES ESPECÍFICAS DA APLICAÇÃO NO CÓDIGO
O CÓDIGO SÓ LIDA COM A INTERFACE Produto
O CÓDIGO PODE PORTANTO FUNCIONAR COM QUALQUER CLASSE ProdutoConcreto
PROVÊ GANCHOS PARA SUBCLASSES
CRIAR OBJETOS DENTRO DE UMA CLASSE COM UM FACTORY METHOD É SEMPRE MAIS FLEXÍVEL DO QUE CRIAR OBJETOS DIRETAMENTE
O FACTORY METHOD PROVÊ UM GANCHO PARA QUE SUBCLASSES FORNEÇAM UMA VERSÃO ESTENDIDA DE UM OBJETO
EXEMPLO NUM EDITOR DE DOCUMENTOS
UMA CLASSE Documento PODERIA TER UM FACTORY METHOD criaFileDialog PARA CRIAR UM OBJETO FILE DIALOG DEFAULT PARA ABRIR UM DOCUMENTO EXISTENTE
UMA SUBCLASSE DE Documento PODERIA CRIAR UM FILE DIALOG ESPECIAL ATRAVÉS DO OVERRIDE DO FACTORY METHOD DEFAULT
NESTE CASO, O FACTORY METHOD NÃO É ABSTRATO MAS FORNECE UM DEFAULT RAZOÁVEL
CONSIDERAÇÕES DE IMPLEMENTAÇÃO
É BOA PRÁTICA USAR UMA CONVENÇÃO DE NOMES PARA ALERTAR PARA O FATO DE QUE ESTÁ USANDO FACTORY METHODS
EXEMPLO: makeABC(), makeXYZ()
EXEMPLO: criaabc(), criaXYZ()
EXEMPLO DE CÓDIGO: CRIAÇÃO DE LABIRINTOS
A CRIAÇÃO DE LABIRINTOS JÁ VISTA NÃO FICOU FLEXÍVEL POIS CRIAMOS (COM new) OS OBJETOS ESPECIFICANDO AS CLASSES CONCRETAS NA FUNÇÃO criaLabirinto
USAREMOS FACTORY METHODS PARA DEIXAR QUE SUBCLASSES ESCOLHAM QUE OBJETOS CRIAR
USAREMOS O SEGUINTE PROJETO
Produto: Sala, Parede, Porta
ProdutoConcreto: Sala, SalaEncantada, SalaPerigosa, Parede, ParedeComBomba, Porta, PortaComChave
Criador: Jogo
SEU MÉTODO criaLabirinto CRIA O LABIRINTO CHAMANDO FACTORY METHODS
ELE TAMBÉM É UM CriadorConcreto POIS OFERECE UMA IMPLEMENTAÇÃO DEFAULT PARA OS FACTORY METHODS (PARA CRIAR UM LABIRINTO SIMPLES)
CriadorConcreto: Jogo, JogoEncantado, JogoPerigoso QUE SERÃO SUBCLASSES DE Jogo
INICIAMOS COM O CRIADOR Jogo QUE CONTÉM OS FACTORY METHODS
classe Jogo { // factory methods com default public Labirinto criaLabirinto() { return new Labirinto(); } public Sala criaSala(int númeroDaSala) { return new Sala(númeroDaSala); } public Parede criaParede() { return new Parede(); } public Porta criaPorta(Sala sala1, Sala sala2) { return new Porta(sala1, sala2); } // Observe que essa função não tem new: ela usa factory methods // Esta é a *única* diferença com relação à versão original public Labirinto criaLabirinto() { Labirinto umLabirinto = criaLabirinto(); Sala sala1 = criaSala(1); Sala sala2 = criaSala(2); Porta aPorta = criaPorta( sala1, sala2 ); umLabirinto.adicionaSala( sala1 ); umLabirinto.adicionaSala( sala2 ); sala1.setVizinho( NORTE, criaParede() ); sala1.setVizinho( LESTE, aPorta ); sala1.setVizinho( SUL, criaParede() ); sala1.setVizinho( OESTE, criaParede() ); sala2.setVizinho( NORTE, criaParede() ); sala2.setVizinho( LESTE, criaParede() ); sala2.setVizinho( SUL, criaParede() ); sala2.setVizinho( OESTE, aPorta ); return umLabirinto; } }
PARA CRIAR UM JOGO PERIGOSO, CRIAMOS UMA SUBCLASSE DE Jogo E REDEFINIMOS ALGUNS FACTORY METHODS
// Um novo CriadorConcreto class JogoPerigoso extends Jogo { public Parede criaParede() { return new ParedeDestruível(); } public Sala criaSala(int númeroDaSala) { return new SalaComBomba(númeroDaSala); } }
COMPARANDO O DIAGRAMA DE OBJETOS COM A VERSÃO INICIAL, NÃO HÁ OBJETO ADICIONAL
SÓ HÁ UMA MUDANÇA DO OBJETO umJogo PARA UM umJogoPerigoso
PARA CRIAR UM JOGO ENCANTADO, PROCEDEMOS DE FORMA ANÁLOGA
// Um novo CriadorConcreto class JogoEncantado extends Jogo { public Sala criaSala(int númeroDaSala) { return new SalaEncantada(númeroDaSala, jogaEncantamento()); } public Porta criaPorta(Sala sala1, Sala sala2) { return new PortaPrecisandoDeEncantamento(sala1, sala2); } protected Encantamento jogaEncantamento() { ... } }