在这次分享之前,我们先来分享一个设计原则------开放-关闭原则。其实在第一篇文章分享策略模式的时候已经使用过了。大家还记得那个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对象即可。