Factory Method Edeyson Andrade Gomes www.edeyson.com.br
Agenda Resumo Quando Usar Participantes Colaborações Conseqüências Exemplo Estrutura Genérica Exercício 2
Resumo Ao invés do cliente instanciar objetos através da chamada a new, ficando acoplado com a classe concreta que ele instancia, ele chama um método de criação (FactoryMethod). 1. carro = new Carro() 2. O objeto carro é uma instância da classe concreta Carro. Isso está pré-determinado neste código. Exemplo Acoplado 3
Resumo Ao invés do cliente instanciar objetos através da chamada a new, ficando acoplado com a classe concreta que ele instancia, ele chama um método de criação (FactoryMethod). E se o objetivo é instanciar carro com uma classe concreta determinada em tempo de execução: 1. carro = criacarro() 2. O método criacarro pode criar um carro tendo como retorno um Carro Abstrato. Logo, criacarro concreto determina qual carro concreto criar. criacarro() é um Factory Method 4
Resumo Usando o Factory Method, uma classe cria um objeto pelo TIPO Abstrato e uma subclasse concreta sua determina qual a classe concreta do objeto a criar. public abstract Assento CriaAssento(); public Assento CriaAssento() { } return new AssentoMadeira() ; Mudar a subclasse concreta que cria o objeto permite mudar a classe do objeto criado 5
Quando Usar Quando uma classe (o criador) não pode antecipar a classe dos objetos que deve criar Quando uma classe quer que suas subclasses especifiquem os objetos criados 6
Participantes Produto Define a interface (o TIPO) dos objetos criados pelo Factory Method ICano ProdutoConcreto Implementa a interface Produto Cano da Tigre Criador Declara o Factory Method que retorna um objeto do tipo Produto Encanamento 7
Participantes Às vezes, o Criador não é abstrato Pode envolver uma classe concreta que tenha uma implementação padrão para o Factory Method para retornar um objeto com algum tipo ProdutoConcreto CriadorConcreto Faz override do Factory Method para retornar uma instância de ProdutoConcreto 8
Colaborações Criador depende de suas subclasses para definir o Factory Method para que ele retorne uma instância do ProdutoConcreto apropriado 9
Conseqüências Factory Method elimina a necessidade de colocar classes específicas da aplicação no código O código só lida com o TIPO Produto O código pode 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 10
Exemplo 11
Estrutura Genérica 12
Exercício Deseja-se criar uma Casa que possua Salas, Quartos, Paredes e Portas, que podem ser construídas em Aço, Vidro ou Madeira. Casa de Aço possui Salas, Quartos, Paredes e Portas de Aço. O projeto inicial da Casa deve ter duas Salas e dois Quartos, com paredes com e sem porta, como apresenta a Figura 01. 13
Exercício Embora simplório, mas didático, o projeto em UML será o seguinte: PortaAB aberta : boolean nomeclasse : Logical View::java::lang::String abrir() fechar() estaaberta() entrar() ParedeAB nomeclasse : Logical View::java::lang::String ParedeAB() ParedeAB() entrar() #porta 0..1 -paredenorte -paredeoeste -paredeleste -paredesul -paredenorte -paredeoeste -paredeleste -paredesul QuartoAB nomeclasse : Logical View::java::lang::String SalaAB nomeclasse : Logical View::java::lang::String entrar() setparedenorte() setparedesul() setparedeleste() setparedeoeste() -quarto1 -quarto2 Casa Casa() entrar() setparedenorte() setparedesul() setparedeleste() setparedeoeste() -sala1 -sala2 14
Exercício Construir Casa de Madeira, de Vidro e de Aço com a estrutura da Figura 01 15
public class CasaMadeira { /* Atributos da Casa */ private QuartoAB quarto1, quarto2; private SalaAB sala1, sala2; /* O construtor CasaMadeira é responsável por determinar a estrutura da Casa e os * Objetos Concretos (de Madeira) a usar em sua construção. Note que Estrutura e * Objeto Concreto da construção estão Acoplados. */ public CasaMadeira() { PortaAB porta = new PortaMadeira(); ParedeAB parede = new ParedeMadeira(porta); sala1 = new SalaMadeira(); sala2 = new SalaMadeira(); quarto1 = new QuartoMadeira(); quarto2 = new QuartoMadeira(); sala1.setparedesul(parede); sala2.setparedenorte(parede); sala1.setparedenorte(new ParedeMadeira()); sala1.setparedeoeste(new ParedeMadeira()); O construtor CasaMadeira() determina TODO objeto concreto a usar através de NEW. Ou seja, todos os objetos concretos são de madeira.. } 16 } porta = new PortaMadeira(); parede = new ParedeMadeira(porta); sala2.setparedeoeste(parede); sala2.setparedesul(new ParedeMadeira()); porta = new PortaMadeira();...
Exercício Vale ressaltar que toda variável definida em CasaMadeira() é definida pelo TIPO ABSTRATO, não por um TIPO CONCRETO. Isso permite variar, no futuro, o tipo concreto a usar. Problemas com a solução: Deseja-se construir uma casa de Vidro com a mesma estrutura da Figura 01. Questão: O que será reusado do código da Casa de Madeira? Vamos à solução! 17
public class CasaMadeira { /* Atributos da Casa */ private QuartoAB quarto1, quarto2; private SalaAB sala1, sala2; public CasaMadeira() { PortaAB porta = new PortaMadeira(); ParedeAB parede = new ParedeMadeira(porta); sala1 = new SalaMadeira(); sala2 = new SalaMadeira(); quarto1 = new QuartoMadeira(); quarto2 = new QuartoMadeira(); sala1.setparedesul(parede); sala2.setparedenorte(parede); sala1.setparedenorte(new ParedeMadeira()); sala1.setparedeoeste(new ParedeMadeira()); porta = new PortaMadeira(); parede = new ParedeMadeira(porta); public class CasaVidro { /* Atributos da Casa */ private QuartoAB quarto1, quarto2; private SalaAB sala1, sala2; public CasaVidro() { PortaAB porta = new PortaVidro(); ParedeAB parede = new ParedeVidro (porta); sala1 = new SalaVidro(); sala2 = new SalaVidro(); quarto1 = new QuartoVidro(); quarto2 = new QuartoVidro(); sala1.setparedesul(parede); sala2.setparedenorte(parede); sala1.setparedenorte(new ParedeVidro()); sala1.setparedeoeste(new ParedeVidro()); porta = new PortaVidro(); parede = new ParedeVidro(porta); } 18 sala2.setparedeoeste(parede); sala2.setparedesul(new ParedeMadeira()); porta = new PortaMadeira();... } sala2.setparedeoeste(parede); sala2.setparedesul(new ParedeVidro()); porta = new PortaVidro();... } }
Exercício Deseja-se, agora, construir uma casa de Aço com a mesma estrutura da Figura 01. Questão: O que será reusado da Casa de Madeira ou Vidro? Vamos à solução (Eclipse) Problemas 1. Se a estrutura da Casa mudar, quantas classes temos de mudar? Resp: Mudar as três. 2. Se quisermos construir Casas de Aço, Madeira e Vidro com um quarto e uma sala, ou seja, uma nova estrutura, quantas classes novas necessitamos? Resp: Três (uma de Madeira, uma de Vidro e uma de Aço). Qual a solução a esses problemas? 19
Exercício Por que a solução está acoplada? Resp: Define-se a estrutura e a classe concreta do objeto concreto a usar através de NEW. Como deixar que em tempo de execução o objeto concreto seja determinado? Resp: usando métodos de criação. Ex. Usar TipoXAbstrato objetox = criaobjetox(); criaobjetox cria um objetox e retorna pelo TIPO. public TipoXAbstrato criaobjetox() {...} 20
Exercício Vamos para a solução desacoplada: Casa2S2Q - cria uma casa com duas Salas e Dois Quartos. Define a Estrutura sem determinar o objeto concreto. Casa2S2Q define a estrutura da casa. São os factory methods que vão determinar, nas subclasses de Casa2S2Q, via override, que objeto concreto criar. Vamos criar uma subclasse de Casa2S2Q para determinar os objetos concretos. Vamos criar uma casa de Madeira. Casa2S2QMadeira especializa Casa2S2Q e determina que os objetos concretos criados nos factory methods são de madeira. Vamos criar uma casa de Madeira com porta de Madeira Ornamentada. Casa2S2QMadeiraOrnamentada especializa Casa2S2QMadeira e determina que a porta é Ornamentada e os objetos concretos criados nos factory methods são de madeira, herdando de Casa2S2QMadeira. 21
Exercício Novas Questões 1.Se a estrutura da Casa mudar, quantas classes temos de mudar? Resp: Mudar somente uma, Casa2S2Q. 2. Se quisermos construir Casas com um quarto e uma sala, quantas classes necessitamos? Resp: Cinco (uma para construir a estrutura, uma subclasse de Madeira, uma subclasse de MadeiraOrnamentada, uma subclasse de Vidro e uma subclasse de Aço). Desafio: Construir Casas com um Quarto e uma Sala já usando o padrão Casa1S1Q no Eclipse 22
Exercício Vamos construir Casa de um Quarto e uma Sala de Madeira. Ao usar extends determinamos que estrutura vamos usar. Os Factory Methods determinam o que? Resp: Os objetos concretos a usar na construção da estrutura. Podemos notar que o código de Casa1S1QMadeira e Casa2S2QMadeira são idênticos. Podemos notar que o código de Casa1S1QAço e Casa2S2QAço são idênticos. 23
Exercício Note que para qualquer casa criada, com qualquer estrutura, suas subclasses serão iguais. Subclasses de Aço implementam os mesmos métodos, independendo da estrutura da Casa. Vamos tirar a responsabilidade de criar o objeto concreto da subclasse e colocar numa classe que é uma Fábrica de Objetos. Faremos Fábricas que constroem objetos de Aço para casas de aço, independendo de estrutura, Fábricas de Madeira, Vidro, etc. 24