Composite (padrão de desenho)
From Wiki**3
O padrão composite organiza os objectos em árvores que representam hierarquias parte-todo. O uso do padrão permite que sejam tratados uniformemente os objectos e as suas composições.
Estrutura
O padrão composite tem a seguinte estrutura de classes:
Exemplo
Um restaurante tem empregadas que apresentam menus aos clientes. Cada empregada dispõe de várias ementas, consoante a hora do dia: assim, existem ementas para pequenos almoços, almoços e jantares, por exemplo.
A Empregada Básica
Uma implementação simples definiria na implementação do conceito empregada as referências para os objectos que representam as ementas.
public class Empregada0 { Ementa _ementaPequenoAlmoço; Ementa _ementaAlmoço; Ementa _ementaLanche; public Empregada0(Ementa ementaPequenoAlmoço, Ementa ementaAlmoço, Ementa ementaLanche) { _ementaPequenoAlmoço = ementaPequenoAlmoço; _ementaAlmoço = ementaAlmoço; _ementaLanche = ementaLanche; } public void printMenu() { printMenu(ementaAlmoço); printMenu(ementaLanche); } public void printMenu(Ementa ementa) { System.out.println(ementa.nome()); for (ItemEmenta item : ementa) System.out.printf("%s, %s%n -- %s%n", item.nome(), item.preço(), item.descrição()); } }
Esta situação conduz a inflexibilidade na manutenção: por exemplo, a adição de novas refeições implica a alteração das empregadas!
Refeições a todas as horas
Uma possÃvel solução é definir as empregadas como tendo, não dois, três, ou um número concreto de ementas, mas sim como tendo a capacidade de gerir um número indeterminado (uma colecção) de ementas.
import java.util.ArrayList; public class Empregada1 { ArrayList<Ementa> _ementas; public Empregada1(ArrayList<Ementa> ementas) { _ementas = ementas; } public void printMenu() { for (Ementa ementa : _ementas) printMenu(ementa); } public void printMenu(Ementa ementa) { System.out.println(ementa.nome()); for (ItemEmenta item : ementa) System.out.printf("%s, %s%n -- %s%n", item.nome(), item.preço(), item.descrição()); } }
Sobremesas ao Jantar
Mas o que aconteceria se se quisesse adicionar uma outra ementa apenas a uma das refeições? Por exemplo, adicionar uma ementa de sobremesas ou uma carta de vinhos à ementa do jantar. A solução anterior não tem limitações ao número de ementas que uma empregada pode gerir. No entanto, adicionar uma ementa à colecção não a associa de forma alguma à ementa do jantar: seria necessário alterar o código da empregada para fazer tal associação: uma situação indesejável. A solução passa por utilizar o padrão de composição (composite): por um lado a ementa do jantar vai ter uma outra ementa como se fosse mais uma entrada, por outro a empregada trata a ementa do jantar como sempre fez (i.e., sem alterações).
public abstract class ComponenteEmenta { public void adicionar(ComponenteEmenta componente) { throw new UnsupportedOperationException(); } public ComponenteEmenta obtémFilho(int i) { throw new UnsupportedOperationException(); } public String nome() { throw new UnsupportedOperationException(); } public String descrição() { throw new UnsupportedOperationException(); } public double preço() { throw new UnsupportedOperationException(); } public boolean vegetariano() { throw new UnsupportedOperationException(); } public void print() { throw new UnsupportedOperationException(); } }
public class Ementa extends ComponenteEmenta { ArrayList<ComponenteEmenta> _componentes = new ArrayList<ComponenteEmenta>(); String _nome; String _descrição; public Ementa(String nome, String descrição) { _nome = nome; _descrição = descrição; } public void adicionar(ComponenteEmenta componente) { _componentes.add(componente); } public ComponenteEmenta obtémFilho(int i) { return _componentes.get(i); } public String nome() { return _nome; } public String descrição() { return _descrição; } public void print() { System.out.printf("%n%s, %s%n", nome(), descrição()); System.out.println("--------------------"); for (ComponenteEmenta componente : _componentes) componente.print(); } }
public class ItemEmenta extends ComponenteEmenta { ArrayList<ComponenteEmenta> _componentes = new ArrayList<ComponenteEmenta>(); String _nome; String _descrição; boolean _vegetariano; double _preço; public ItemEmenta(String nome, String descrição, boolean vegetariano, double preço) { _nome = nome; _descrição = descrição; _vegetariano = vegetariano; _preço = preço; } public void adicionar(ComponenteEmenta componente) { _componentes.add(componente); } public ComponenteEmenta obtémFilho(int i) { return _componentes.get(i); } public String nome() { return _nome; } public String descrição() { return _descrição; } public double preço() { return _preço; } public boolean vegetariano() { return _vegetariano; } public void print() { System.out.print(nome()); if (vegetariano()) System.out.print(" (v)"); System.out.println(", " + preço()); System.out.println(" -- " + descrição()); } }
public class Empregada2 { private ComponenteEmenta _ementas; public Empregada2(ComponenteEmenta ementas) { _ementas = ementas; } public void printMenu() { _ementas.print(); } }
public class Restaurante { public static void main(String[] args) { ComponenteEmenta pequenoAlmoço = new Ementa("PEQUENO ALMOÇO", "Pequeno Almoço"); ComponenteEmenta almoço = new Ementa("ALMOÇO", "Almoço"); ComponenteEmenta lanche = new Ementa("LANCHE", "Lanche"); ComponenteEmenta jantar = new Ementa("JANTAR", "Jantar"); ComponenteEmenta sobremesas = new Ementa("SOBREMESAS", "Hmmmm!"); ComponenteEmenta refeições = new Ementa("REFEIÇÕES", "Tudo!"); refeições.adicionar(pequenoAlmoço); refeições.adicionar(almoço); refeições.adicionar(lanche); refeições.adicionar(jantar); jantar.adicionar(new ItemEmenta("Esparguete", "Esparguete com deliciosas alcaparras", true, 5.49)); jantar.adicionar(sobremesas); Empregada2 empregada = new Empregada2(refeições); empregada.mostrarEmentas(); } }