一. 概述
- 定义
动态的将新功能附加到对象上,在对象功能扩展方面,它比继承更有弹性,也体现了开闭原则(ocp)。 - 原理
装饰器模式就像打包一个快递。
Component主体/被装饰者):比如:陶瓷、衣服 和包装两部分;
Decorator包装:比如:报纸填充、塑料泡沫、纸板、木板
UML类图如下:
在Component与ConcreteComponent之间,如果ConcreteComponent类很多,还可以设计一个缓冲层,将共有的部分提取出来,抽象层一个类 。
其实,装饰器模式可以看成是对一个类的一种增强手段。从Decorator的类图来看,内部持有Component实例,真正执行方法时会调用Component实例方法和增强逻辑,这样,就相当于给Component方法增强了,整个的执行原理可以简单浓缩成一句话:是你(extends),还有你(持有实例),一切拜托你(调用实例方法)。
二. 场景示例
星巴克咖啡订单项目(咖啡馆):
- 咖啡种类/单品咖啡:Espresso(意大利浓咖啡)、ShortBlack、LongBlack(美式
咖啡)、Decaf(无因咖啡) - 调料:Milk、Soy(豆浆)、Chocolate
- 要求在扩展新的咖啡种类时,具有良好的扩展性、改动方便、维护方便
- 使用OO( 面向对象(Object Oriented,OO))的来计算不同种类咖啡的费用: 客户可以点单品咖啡,也可以单品咖啡+调料组合。
-
装饰者模式解决星巴克咖啡订单
-
装饰者模式下的订单:2份巧克力+一份牛奶的LongBlack
- Milk包含了LongBlack
- 一份Chocolate包含了(Milk+LongBlack)
- 一份Chocolate包含了(Chocolate+Milk+LongBlack)
- 这样不管是什么形式的单品咖啡+调料组合,通过递归方式可以方便的组合和维护。
- 代码示例:
public abstract class Drink {
public String des; // 描述
private float price = 0.0f;
public String getDes() {
return des;
}
public void setDes(String des) {
this.des = des;
}
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
//计算费用的抽象方法
//子类来实现
public abstract float cost();
}
public class Coffee extends Drink {
@Override
public float cost() {
return super.getPrice();
}
}
public class ShortBlack extends Coffee{
public ShortBlack() {
setDes(" shortblack ");
setPrice(4.0f);
}
}
public class DeCaf extends Coffee {
public DeCaf() {
setDes(" 无因咖啡 ");
setPrice(1.0f);
}
}
public class Espresso extends Coffee {
public Espresso() {
setDes(" 意大利咖啡 ");
setPrice(6.0f);
}
}
public class LongBlack extends Coffee {
public LongBlack() {
setDes(" longblack ");
setPrice(5.0f);
}
}
public class Decorator extends Drink {
private Drink obj;
public Decorator(Drink obj) { //组合
this.obj = obj;
}
@Override
public float cost() {
// getPrice 自己价格
return super.getPrice() + obj.cost();
}
@Override
public String getDes() {
// obj.getDes() 输出被装饰者的信息
return des + " " + getPrice() + " && " + obj.getDes();
}
}
//具体的Decorator, 这里就是调味品
public class Chocolate extends Decorator {
public Chocolate(Drink obj) {
super(obj);
setDes(" 巧克力 ");
setPrice(3.0f); // 调味品 的价格
}
}
public class Milk extends Decorator {
public Milk(Drink obj) {
super(obj);
setDes(" 牛奶 ");
setPrice(2.0f);
}
}
public class Soy extends Decorator{
public Soy(Drink obj) {
super(obj);
setDes(" 豆浆 ");
setPrice(1.5f);
}
}
public class Client {
public static void main(String[] args) {
// 模拟客户点单:2份巧克力+一份牛奶的LongBlack
Chocolate cho = new Chocolate(new Chocolate(new Milk(new LongBlack())));
System.out.println(cho.cost()); //13.0
}
}
三. 装饰器模式在JDK中的应用
JDK中的IO流设计就用到了装饰器模式。
比如:想读取一个二进制文件,可以这样获取一个输入流:
InputStream input = new DataInputStream(new BufferedInputStream(new FileInputStream("C:/test.exe")));
以如下代码为例从源码层面说明JDK中是如何应用装饰器模式的:
DataInputStream dis = new DataInputStream(new FileInputStream("D:\\test.txt"));
// 是一个抽象类,即Component,类似前面的Drink
public abstract class InputStream implements Closeable {}
// InputStream子类,类似前面的LongBlack,DeCaf
public class FileInputStream extends InputStream{ }
// InputStream子类,是一个装饰者类Decorator
public class FilterInputStream extends InputStream {
/**
* The input stream to be filtered.
* 被装饰的对象
*/
protected volatile InputStream in;
}
// InputStream子类,具体的修饰者,类似前面的Milk,Soy
public class DataInputStream extends FilterInputStream implements DataInput{ }
关于类的增强扩展:
Proxy 遇上 Decorator:https://my.oschina.net/huangyong/blog/162655