设计模式之装饰者模式(一)

   现在有一家咖啡厅,里面有四种不同的咖啡种类,而每种不同的咖啡都可以附加牛奶、豆浆、奶泡或者摩卡,而且每次客人点餐,还可以附加多倍的某种配料,比如双倍摩卡。如果使用简单的类继承方法,就要写数量庞大的类,而且很难管理类似于“双倍摩卡”这种形式。

  于是,我们就可以引入装饰模式,装饰模式动态地将责任附加到对象上,扩展功能时,提供了比继承更有弹性的替代方案,

  装饰模式的类图如下:

 

  装饰模式的特点是

   1.装饰者必须能取代被装饰者,也就是说装饰者和被装饰者要用共同的父类。上图的ConcreteComponent类就是被装饰者,ConcreteDecoratorA和B就是装饰者

   2.装饰者中含有一个其父类的引用,用来表明被装饰者或者其他装饰者。在装饰者的方法中,往往要先调用这个引用的同名方法。

 比如,现在我要一杯摩卡奶泡深度烘焙咖啡,计算价格的过程应该是这样的。

 

 Whip中保存的是Mocha的引用,Mocha中保存的是DarkRoast(被装饰者)的引用,通过循环调用,最后就计算出了价格。

 一开始我的疑问是,为什么装饰者必须能取代被装饰者,如果被装饰者和装饰者的父类不一样,会怎么样呢?

  思考了以下以后发现,如果被装饰者和装饰者的父类不一样,这意味着装饰者内部必须还要保存一个被装饰者的引用,而假如需要双重和多重的装饰者,则多个装饰者无法联系在一起,被装饰者也不知道其装饰者的存在。

 代码参考如下:

 Beverage类

/**
饮料类,是装饰者和被装饰者都继承的类。
**/
public abstract class Beverage
{
	String description="unknown";
	public String getDescription()
	{
		return description;
	}
	public abstract double cost();

}

 CondimentDecorator类

/**
装饰者类,所有装饰者都继承自这个类
**/
public abstract class CondimentDecorator extends Beverage
{

	public abstract String getDescription();
	

}  

 DarkRoast类

/**
被装饰者类,表示其中一种咖啡(深度烘焙)
**/
public  class DarkRoast extends Beverage
{
	public DarkRoast()
	{
		this.description="DarkRoast";
	}
	public double cost()
	{
		return 0.99;
	}

}  

还有其他的被装饰者类,Decas,Espresso, HouseBlend的代码省略

Milk类

/**
装饰者,继承了CondimentDecorator,表示其中一个装饰者
**/
public class Milk extends CondimentDecorator
{
	Beverage beverage;//用一个实例变量记录被装饰者
	public Milk(Beverage beverage)//通过构造方法把被装饰者的引用传递过来
	{
		this.beverage=beverage;
	}
	public String getDescription()
	{
		return beverage.getDescription()+",Milk";
	}
	public double cost()//先计算被装饰对象的价钱,再加上该装饰品的价钱。
	{
		return 0.10+beverage.cost();
	}
}

  其他的装饰者类,Mocha,Soy,Whip的代码省略

测试类

package com.qingfei.decorator;
public class Test 
{
	public static void main(String[] args)
	{
		Beverage beverage=new Espresso();
		System.out.println(beverage.getDescription()+"$"+beverage.cost());
		Beverage beverage2=new DarkRoast();
		beverage2=new Mocha(beverage2);
		beverage2=new Whip(beverage2);
		beverage2=new Soy(beverage2);
		System.out.println(beverage2.getDescription()+"$"+beverage2.cost());

	}
}

输出结果是

 这样一来,就可以动态地为被装饰者添加装饰者了,当要扩展的时候,只需要增加一个装饰者的类即可,而装饰者类的修改对于被装饰者类没有影响,符合了开闭原则。

posted on 2015-01-21 11:38  qingfei  阅读(130)  评论(0编辑  收藏  举报