Padrão Decorator - Livro HeadFirst

Gente, comecei a estudar design patterns com o livro head first - design patterns. No capítulo 3, onde é abordado o padrão Decorator, o autor implementa um sistema para uma lanchonete que vende cafés.

Só para explicar para quem não tem o livro, a lanchonete cobra pelo café de acordo com o tipo de café e com os condimentos adicionados a ele.

Assim, o cliente pede um café Expresso, que custa 1 real e adiciona leite (.50 centavos), chocolate (.20) e “whip” (.30).

Então como resultado ele mostra o preço: 2 reais e a lista de condimentos adicionados: "café expresso + chocolate, leite e “whip”.

Para implementar o negocio ele usou o padrão Decorator, fazendo algo como é mostrado no diagrama de classe da imagem em anexo.

As classes dos tipos de café ele implementou ± assim:

public Espresso ()
{
    description = "Expresso";
}
public double cost()
{
   return 1.99;
}

Os métodos cost() e getDescription() das classes CondimentDecorator são implementadas assim:

public Mocha(Beverage beverage)
{
      this.beverage = beverage;
}

public double cost ()
{
   return .20 + beverage.cost();
}

public String getDescription()
{
   return beverage.getDescription() + ", milk";
}

nas classes abstratas não há muita implementação.

e na classe em que o programa é executado é feito isso:

Beverage beverage = new Espresso();
beverage = new Mocha(beverage);
beverage = new Whip (beverage); 

Ai a cada vez que ele cria uma instância de um condimento o preço é adicionado ao condimento.

eu tentei implementar de outro jeito:

public interface Beverage {
	
	public String getDescription();	
	public double getCost();
	public void setDescription(String description);
	public void setCost(double cost);
	
}


public class PureBeverage implements Beverage{
	
	private String description = "unknow";
	private double cost = .0;
	
	private ArrayList <CondimentDecorator> condiments = new  ArrayList <CondimentDecorator> ();  
	
	public void setCost(double cost) {
		this.cost = cost;			
	}
	
	public void setDescription(String description) {
		this.description = description;		
	}
	
	public String getDescription()
	{
		return description;
	}
	public double getCost ()
	{
		return cost;
	}
	
	public void addCondiment (CondimentDecorator decorator)
	{
		this.cost += decorator.getCost();
		this.description += decorator.getDescription();
		condiments.add(decorator);
	}
}


public class Espresso extends PureBeverage{	
	public Espresso()
	{
		setCost(.75);
		setDescription("Café expresso ");				
	}		
}

public class CondimentDecorator implements Beverage{
	private double cost; 
	private String description;
	
	public double getCost() {		
		return cost;
	}
	public String getDescription() {
		
		return description;
	}
	public void setCost(double cost) {
		this.cost = cost;
		
	}
	public void setDescription(String description) {
		this.description = description;		
	}

}

public class Milk extends CondimentDecorator{
	
	public Milk ()
	{
		setCost(.20);
		setDescription(", milk");		
	}	
}



public class StarbuzzCafe {

	
	public static void main(String[] args) {

		PureBeverage beverage = new Decaf();
		beverage.addCondiment(new Mocha());
		beverage.addCondiment(new Milk());
		beverage.addCondiment(new Whip());		
		System.out.println("Descrição: "+beverage.getDescription());
		System.out.println("Preço: "+beverage.getCost());		
		
	}
}

Achei que desta forma seria mais intuitivo, porque ai fica explícito que estou adicionando condimentos. Olhando por cima, pela minha pouca experiência que tenho com essas coisas, acho que não haveria muitos problemas de manutenção depois, se for preciso adicionar novos tipos de bebidas ou novos condimentos.

Perguntas:

Vocês conseguem indentificar algum problema no jeito como eu implementei?

Do jeito que eu fiz o negocio, continua sendo padrão decorator ou deixou de ser?

Tem alguma forma de melhorar isso tudo?


alex, eu também tenho este livro e estava dando uma olhada neste padrão de projeto, achei o exemplo do livro meio infeliz, acredito que tenhas tido a mesma impressão…

também não gostei muito do exemplo, lindermann.
confundiu um pouco as coisas. mas vi exemplos em outros livros que esclareceram melhor.

agora entendi, lucas.
não tava entendendo direito o propósito desse pattern. mas agora eu vi que ele adiciona novas funcionalidades ao objeto. do jeito que eu estava fazendo tava dando mais importância aos dados do que ao comportamento dos objetos si.

valeu!

Eu acho que a sua alternativa não é um decorator. Nesse exemplo específico, a funcionalidade fica equivalente, mas o decorator é por princípio muito mais flexível.

O seu ConcreteComponent (PureBeverage) depende diretamente da classe do “decorator” (CondimentDecorator), o que limita bastante as capacidades do decorator. Não se pode, por exemplo, criar um decorator que multiplique o preço (ClienteEhRicoDecorator) ao invés de somar ou um outro decorator que altere a string de description para adicionar um símbolo de TM ou algo assim.

é tem razão.
não tinha visto a coisa por esse lado … :roll:

O Rafael verificou um ponto importante na sua implementação do Decorator.

Realmente dessa forma você está dizendo que sempre uma PureBeverage terá CondimentDecorator, que quebra a idéia do Decorator, onde não deveremos ter depêndencias entre classes.

A idéia do Decorator é que você adicione funcionalidades de forma genérica a um determinado tipo de objeto, por exemplo, classes do pacote io do java usam decorator, um outro exemplo é o Scroll do Swing, vc adiciona um Component ao Scroll, incrementando assim uma funcionalidade a mais no componente, se você quiser criar ou outro Decorator que incremente o Scroll, fazendo com que ele faça parte de um painel 3D, vc pode e ficaria fácil, assim como também se você quisesse não mais que o componente tenha o Scroll, bastaria não mais usar a classe Scroll.

Bem… é um post antigo este, mas, a quem interessar possa, tem uma discussão aprofundada sobre o Decorator aqui:

Quanto ao exemplo apresentado aí, também discordo que seja Decorator! Abraços.

Oi, marconems

Tb acho q esta implementação do colega alexswb deixou de seguir o pattern ‘Decorator’. O adotou o Princípio ‘Composition over Inheritance’, então Creio q ela segue + o Padrão ‘Observer’ (talvez não filosificamente, mas no código sim), pq toda vez q 1 objeto “Bebida” adiciona 1 “Condimento” é como se a Bebida estivesse notificando o Condimento: hei Condimento, estou adicionando 1 do seu “tipo” aki em mim.
O q acha??!

Não sei se ajuda, mas no link abaixo tem um exemplo do uso do Decorator:

http://www.patternizando.com.br/2011/01/padrao-de-projeto-decorator-uma-aplicacao-real-em-java/

Não sei se é bem um problema, mas vc usa composição para adicionar múltiplos decorators. A idéia é vc ter uma referência ao objeto encapsulado. E esse objeto, se for um Decorator, ter uma referência ao próximo e assim por diante. Vai um dentro do outro, indefinidamente. No seu caso, vc tem todos dentro do primeiro Decorator.

Eu acho que sim, porque a sua classe Decorator continua sendo da mesma classe que o objeto Decorado, ou implementando a mesma interface do objeto Decorado.

Independente de como vc fez, isso aqui tem que funcionar:

PureBeverage beverage = new Decaf();
beverage = new Mocha();

Bom, acho que é isso. No próprio livro tem um exemplo mais feliz, mostrando onde usaram Decorator na API do Java.

Observações e correções são bem vindas.