装饰者模式
装饰模式就是给一个对象增加一些新的功能,而且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例
动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。
设计原则
- 类应该对扩展开放,对修改关闭。
案例
这里我们采用一个制作饮料的过程来介绍装饰者模式,我们先来分析一下在饮料制作的过程中有哪些东西是经常变动的,比如饮料是在牛奶里面加巧克力,蜂蜜等等,也有可能是往红茶中添加柠檬之类的,诸如此类,这样如果我们在设计基础饮料时添加太多的配料属性的话,后面如果我们想要更灵活地添加新的配料就很麻烦了。
我们采用装饰者模式,所有的如牛奶,红茶等等这一类主原料,全部继承于一个抽象的饮料类,然后所有的配料再继承于一个抽象的配料类,而抽象的配料类也同时继承抽象的饮料类。这里的主原料则是一个被装饰者,而配料则是装饰者。
例如:主原料的基础抽象类Beverage(饮料)
/**
* 饮料类
* 装饰者模式中的被装饰者基类
*
*/
public abstract class Beverage {
String description = "Unknown Beverage";
public String getDescription() {
return description;
}
public abstract double cost();
}
然后是配料类的抽象类CondimentDecorator,注意装饰者类需要继承被装饰者的基类或接口
/**
* 装饰者模式中的装饰者的抽象基类
*
*/
public abstract class CondimentDecorator extends Beverage {
public abstract String getDescription();
}
一个被装饰者类
/**
* 具体的被装饰者实现类
* 浓缩咖啡Espresso
*
*/
public class Espresso extends Beverage {
public Espresso() {
description = "Espresso";
}
@Override
public double cost() {
return 1.99;
}
}
被装饰者类
/**
* 具体的被装饰者实现类
*
*/
public class HouseBlend extends Beverage {
public HouseBlend() {
description = "House Blend Coffe";
}
@Override
public double cost() {
return 0.8;
}
}
具体的装饰者类,注意这里继承了装饰者的基类,而这个基类也继承了被装饰者基类,故装饰者类需要继承被装饰者类或实现接口;并且在装饰者类中需要持有被装饰者对象实例。
/**
* 装饰者类
* 摩卡
*
*/
public class Mocha extends CondimentDecorator {
Beverage beverage;
public Mocha(Beverage beverage) {
this.beverage = beverage;
}
@Override
public String getDescription() {
return beverage.getDescription() + ", Mocha";
}
@Override
public double cost() {
return 0.2 + beverage.cost();
}
}
/**
* 装饰类
* 巧克力
* @author 37111
*
*/
public class Chocolate extends CondimentDecorator {
Beverage beverage;
public Chocolate(Beverage beverage) {
this.beverage = beverage;
}
@Override
public String getDescription() {
return beverage.getDescription() + ",chocolate";
}
@Override
public double cost() {
return 0.5 + beverage.cost();
}
}
测试:
public class Test {
public static void main(String[] args) {
Beverage b1 = new Espresso();
b1 = new Mocha(b1);
b1 = new Chocolate(b1);
System.out.println(b1.getDescription() + "$" + b1.cost());
Beverage b2 = new HouseBlend();
b2 = new Chocolate(b2);
System.out.println(b2.getDescription() + "$" + b2.cost());
}
}
结果:
Espresso, Mocha,chocolate$2.69
House Blend Coffe,chocolate$1.3
这里我们可以看到使用装饰者模式的好处,当需要添加更多的配料时,只需要再写相应的继承了被装饰者基类的配料类,并且当我们修改任意的配料类的时候,所有的结果也就跟着一起修改了,这样也就实现了类之间的解耦合。
实际中的应用
在java的API中使用到装饰者模式的地方有很多,java的I/O流中就使用了装饰者模式BufferedInputStream及LineNumberInputStream都扩展自FilterInputStream,而FilterInputStream是一个抽象的装饰类。