0x0、引言
临近周末,啃多一节《设计模式之美》,本文对应设计模式与范式:结构型(50),装饰器模式 (Decorator Pattern)。
装饰器模式和上节学的**桥接模式**(分离实体和行为) 代码结构非常相似,都是用组合来扩展原有类,但解决的问题大不相同。
Tips:二手知识加工难免有所纰漏,感兴趣有时间的可自行查阅原文,谢谢。
0x1、定义
允许动态地向一个现有的对象添加新功能,同时不改变其结构,相当于对现有对象的进行了一个包装。
很好理解,就是套了一层,跟代理模式又不一样,装饰器模式可以套娃一样套多层。
0x2、写个例子
桥接模式 → 广度,装饰器模式 → 深度,咋体现?还是上节形状的例子:
abstract class Shape {
abstract void show();
}
// 形状
public class Circle extends Shape {
@Override void show() { System.out.println("圆形"); }
}
public class Square extends Shape {
@Override void show() { System.out.println("矩形"); }
}
// 引入颜色
public class RedCircle extends Shape {
@Override void show() { System.out.println("红色圆形"); }
}
public class RedSquare extends Shape {
@Override void show() { System.out.println("红色矩形"); }
}
public class BlueCircle extends Shape {
@Override void show() { System.out.println("蓝色圆形"); }
}
public class BlueSquare extends Shape {
@Override void show() { System.out.println("蓝色矩形"); }
}
上节是形状或者颜色变多,现在不是,是需求开始变多,比如添加 质感:磨砂和光滑
public class SmoothRedCircle extends RedCircle // 光滑红色圆形
public class SmoothRedSquare extends RedSquare // 光滑红色矩形
public class SmoothBlueCircle extends BlueCircle // 光滑蓝色圆形
public class SmoothBlueSquare extends BlueSquare // 光滑蓝色矩形
public class MatteRedCircle extends RedCircle // 磨砂红色圆形
public class MatteRedSquare extends RedSquare // 磨砂红色矩形
public class MatteBlueCircle extends BlueCircle // 磨砂蓝色圆形
public class MatteBlueSquare extends BlueSquare // 磨砂蓝色矩形
可以,子类变成8个了,接着添加 大小:大中小,子类会变成8*3=24个,再加?直接类爆炸~
这种深度多层继承的场景,用装饰器模式就很适合了:
// 抽象组件(接口和抽象类都可以)
interface IShape {
String show();
}
// 抽象装饰类(内部有一个指向组件对象的引用,用来调装饰前对象的方法)
public abstract class BaseDecorator implements IShape {
private IShape shape;
public BaseDecorator(IShape shape) { this.shape = shape; }
@Override public String show() { return shape.show(); }
}
// 具体组件类
public class CircleShape implements IShape {
@Override public String show() { return "圆形"; }
}
public class SquareShape implements IShape {
@Override public String show() { return "矩形"; }
}
// 颜色具体装饰类(可调用抽象装饰类中定义的方法,也可新增方法来扩展对象行为)
public class RedDecorator extends BaseDecorator {
public RedDecorator(IShape shape) { super(shape); }
@Override public String show() { return "红色" + super.show(); }
}
public class BlueDecorator extends BaseDecorator {
public BlueDecorator(IShape shape) { super(shape); }
@Override public String show() { return "蓝色" + super.show(); }
}
// 材质具体装饰类
public class SmoothDecorator extends BaseDecorator {
public SmoothDecorator(IShape shape) { super(shape); }
@Override public String show() { return "光滑" + super.show(); }
}
public class MatteDecorator extends BaseDecorator {
public MatteDecorator(IShape shape) { super(shape); }
@Override public String show() { return "磨砂" + super.show(); }
}
// 大小具体装饰类
public class BigDecorator extends BaseDecorator {
public BigDecorator(IShape shape) { super(shape); }
@Override public String show() { return "大" + super.show(); }
}
public class MiddleDecorator extends BaseDecorator {
public MiddleDecorator(IShape shape) { super(shape); }
@Override public String show() { return "中" + super.show(); }
}
public class SmallDecorator extends BaseDecorator {
public SmallDecorator(IShape shape) { super(shape); }
@Override public String show() { return "小" + super.show(); }
}
// 测试用例
public class DecoratorTest {
public static void main(String[] args) {
IShape circle = new CircleShape();
IShape square = new SquareShape();
IShape redCircle = new RedDecorator(circle);
IShape smoothBlueSquare = new SmoothDecorator(new BlueDecorator(square));
IShape bigMatteRedCircle = new BigDecorator(new MatteDecorator(redCircle));
System.out.println(circle.show());
System.out.println(square.show());
System.out.println(redCircle.show());
System.out.println(smoothBlueSquare.show());
System.out.println(bigMatteRedCircle.show());
}
}
运行输出结果如下:
用继承要24个子类,用装饰器模式只要10个,顺带画出UML类图:
四个角色:
- Component (抽象组件) → 声明具体组件实现的业务方法,让客户端以一致的方式处理为修饰和修饰后的对象;
- ConcreteComponent (具体组件) → 抽象组件的具体实现;
- Decorator (抽象装饰类) → 包含对组件的引用,并重写抽象组件的方法;
- ConcreteDecorator (具体装饰类) → 抽象装饰类的具体实现,除了重写方法外,还可以添加附加功能;
适用场景:
- 在不影响其他对象的情况下,快速动态透明地为单个对象添加功能;
- 不支持继承扩展类的场景,如final关键字修饰的类;
优点:快速扩展对象功能,比继承灵活,不会导致类个数急剧增加,动态增删对象实例功能,按需按顺序组合功能;
缺点:
- 顺序调用链装饰时,删除某个装饰器需要修改上下文代码;
- 容易增加很多装饰对象,增加代理理解难度;
- 和桥接模式一样,组合相比继承,更不容易找到对象间的调用关系;
附:装饰器模式应用示例 → Java IO类库
在网上找了张Java IO的继承树形图:
两个变化维度:字节流 or 字符流,输入流 or 输出流,组合出四个子类:InputStream、OutputStream、Reader和Writer,针对不同的读写场景,从这四个父类的基础上,又扩充出很多子类。
在日常使用时,又涉及到了一个 低级流 和 高级流 的概念,高级流不能直接使用,需要传入低级流,裹好几层,如:
InputStream in = new FileInputStream("/user/jie/test.txt");
InputStream bin = new BufferedInputStream(in);
DataInputStream din = new DataInputStream(bin);
int data = din.readInt();
原理就是采用了装饰器模式,低级流可以按需进行功能增强,如上面的支持缓存,支持按照基本类型读取数据。可以打开这三个类看看:
可以看到,都继承了InputStream类,中间两个类的构造函数传递的InputStream的实例:
看下DataInputStream.readInt()方法:
int是4个字节,所以这里读了4次,而代码中传入的in是BufferedInputStream的,会调它的read()方法:
fill()填充缓冲区,最后返回一个字节数组。
另外,不知道眼尖的你有没有发现,这两个类不是直接继承InputStream,而是继承自FilterInputStream,这样做的原因是:
有些装饰器本身不需要真正处理read()等方法,但是装饰器模式的 链式传递,不用到也要实现这些方法。而每个这样的装饰器都重写方法的话,会存在大量重复代码。用一个装饰器父类FilterInputStream提供默认实现,以此减少这些重复代码。