设计模式第四天之装饰者模式

   在这次分享之前,我们先来分享一个设计原则------开放-关闭原则。其实在第一篇文章分享策略模式的时候已经使用过了。大家还记得那个android的图片缓存吗?大家发现,如果我要新增加一种缓存机制,只需要增加一个类(这个类实现ImageCache接口即可),然后然后通过setImageCache()方法将该类对象赋值给ImageLoader的ImageCache对象,就可以使用了。大家有没有发现?我在新增这个缓存机制的过程中只是新增了一个类,并没有对原有的代码进行修改,这就是遵循了开放-关闭原则。

   开放-关闭原则,我们的目标是允许类容易扩展,在不修改现有代码的情况下,就可搭配新的行为。如能实现这样的目标,有什么好处呢?这样的设计具有弹性,可以应对改变,可以接受新的功能类应对改变的需求。(引用《Head First设计模式》)。

   我这次分享的装饰着模式是完全遵循开放-关闭原则的。

用《Head First设计模式》中的例子来描述吧。

需求是这样的?我们要为一个咖啡店设计一个订单系统。该咖啡店有几种(HouseBlend、DarkRoast、Decaf,Espresso)原汁原味的咖啡,都是原味的。然后顾客购买咖啡的时候,可以指定添加调料,调料有蒸奶(Steamed Milk)、豆浆(Soy)、摩卡(Mocha)和覆盖奶泡。

添加调料可以按照客户的要求添加多种,当然也可以不添加。然后计算出价钱(四种原味的咖啡要钱,调料也要钱)

 大家可以想想你们的饿实现。下面我来分享一个装饰者模式的实现。

如果顾客想要摩卡和奶泡深焙咖啡。

1.      拿一个深焙咖啡(DarkRoast)对象

2.      以摩卡(Mocha)对象装饰它

3.      以奶泡(Whip)对象装饰它

4.      调用cost()方法,并依赖委托delegate将调料的价钱加上去。

《Head First设计模式》中的图。

首先画出UML建模图。


根据uml我们知道所有的主体或者装饰者都要间接或直接实现相同的接口,这也是装饰者模式所要求的。

首先来实现饮料的接口Bevarage,有最基本的获取描述和计算价钱的方法


/**
 * 饮料和调料的公共的接口
 * @author wangpeiyu
 * @version 1.0
 * @created 06-11月-2016 10:41:31
 */
public interface Beverage {

	public double cost();

	public String getDescription();

}

基于饮料的接口,我们实现四种饮料


/**
 * DarkRoast原味饮料
 * 该设计模式中为主体
 * @author wangpeiyu
 * @version 1.0
 * @created 06-11月-2016 10:41:38
 */
public class DarkRoast implements Beverage {

	private String des;
	private double price;

	public DarkRoast(){
		des="Dark Roase";
		price=1.1;
	}

	@Override
	public double cost(){
		return price;
	}
	@Override
	public String getDescription(){
		return des;
	}
}

/**
 *  * Decaf原味饮料
 * 该设计模式中为主体
 * @author wangpeiyu
 * @version 1.0
 * @created 06-11月-2016 10:41:39
 */
public class Decaf implements Beverage {

	private String des;
	private double price;

	public Decaf(){
		des="Decaf";
		price=1.15;
	}

	@Override
	public double cost(){
		return price;
	}

	@Override
	public String getDescription(){
		return des;
	}
}

/**
 *  * Espresso原味饮料
 * 该设计模式中为主体
 * @author wangpeiyu
 * @version 1.0
 * @created 06-11月-2016 10:41:41
 */
public class Espresso implements Beverage {

	private String des;
	private double price;

	public Espresso(){
		des="Espresso";
		price=1.2;
	}
	@Override
	public double cost(){
		return price;
	}

	@Override
	public String getDescription(){
		return des;
	}
}

/**
 *HouseBlend原味饮料
 * 该设计模式中为主体
 * @author wangpeiyu
 * @version 1.0
 * @created 06-11月-2016 10:41:36
 */
public class HouseBlend implements Beverage {

	private String des;
	private double price;

	public HouseBlend(){
		des="HouseBlend";
		price=1.3;
	}

	@Override
	public double cost(){
		return price;
	}

	@Override
	public String getDescription(){
		return des;
	}
}

}

通过前面的两个图,我们知道调料作为装饰者,在类中要有一个Beverage饮料接口的一个对象,用来指向前面最新饮料的对象,这样在算钱的时候,可以先计算出前面的,然后在加上现在增加的。获取描述也是一样,先获取前面的描述再加上现在的描述。那首先我们来实现调料的抽象类。我们知道每个调料类都有Beverage接口指向前面的饮料,那我们可以直接在抽象类中写这个Beverage对象,然后在各自的调料类中赋值便可以了。(当然price价钱和des描述也是可以这样做的。)

调料类接口:

/**
 * 调料的接口
 * @author wangpeiyu
 * @version 1.0
 * @created 06-11月-2016 10:41:42
 */
public abstract class  CondimentDecorator implements Beverage {

	protected Beverage beverage;
	@Override
	public abstract double cost();
	
	@Override
	public abstract String getDescription();

}

下面是各种调料。

/**
 * 调料
 * @author wangpeiyu
 * @version 1.0
 * @created 06-11月-2016 10:41:43
 */
public class Milk extends CondimentDecorator {

	private String des;
	private double price;

	/**
	 * 
	 * @param beverage
	 */
	public Milk(Beverage beverage){
		this.beverage = beverage;
		des="Milk";
		price = 0.5;
	}
	@Override
	public double cost(){
		return beverage.cost()+price;//前面的饮料的价钱,再加上当前增加的
	}
	@Override
	public String getDescription(){
		return beverage.getDescription()+" "+des;
	}
}

/**
 * 调料
 * @author wangpeiyu
 * @version 1.0
 * @created 06-11月-2016 10:41:44
 */
public class Mocha extends CondimentDecorator {

	private String des;
	private double price;
	/**
	 * 
	 * @param beverage
	 */
	public Mocha(Beverage beverage){
		this.beverage = beverage;
		des="Mocha";
		price=0.6;
	}

	@Override
	public double cost(){
		return beverage.cost()+price;
	}
	@Override
	public String getDescription(){
		return beverage.getDescription()+" "+des;
	}
}

 * @created 06-11月-2016 10:41:45
 */
public class Soy extends CondimentDecorator {

	private String des;
	private double price;
	/**
	 * 
	 * @param beverage
	 */
	public Soy(Beverage beverage){
		this.beverage = beverage;
		des="Soy";
		price=0.4;
	}

	@Override
	public double cost(){
		return beverage.cost()+price;
	}
	@Override
	public String getDescription(){
		return beverage.getDescription()+" "+des;
	}
}

/**
 * 调料
 * @author wangpeiyu
 * @version 1.0
 * @created 06-11月-2016 10:41:47
 */
public class Whip extends CondimentDecorator {

	private String des;
	private double price;
	/**
	 * 
	 * @param beverage
	 */
	public Whip(Beverage beverage){
		this.beverage = beverage;
		des="Whip";
		price=0.45;
	}

	@Override
	public double cost(){
		return beverage.cost()+price;
	}
	@Override
	public String getDescription(){
		return beverage.getDescription()+" "+des;
	}
}

下面来写一个测试的例子。

public class main {
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		//新建一种饮料
		Decaf beverage = new Decaf();
		//原味的钱
		System.out.println(beverage.getDescription()+" $"+beverage.cost());
		//添加了牛奶调料,将主体传递进去
		Beverage beverage2 = new Milk(beverage);
		//添加Mocha调料,这时候将前面的最新的饮料传递进去
		beverage2 = new Mocha(beverage2);
		//添加Whip饮料,又将最新的饮料传递进去
		beverage2 = new Whip(beverage2);
		//查看当前饮料的价钱
		System.out.println(beverage2.getDescription()+" $"+beverage2.cost());
	}
}

结果是:



这样就实现了客户想加什么调料,直接新建一个给调料的类,并将之前的饮料传给调料里面的beverage对象即可。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章