把书读薄 | 《设计模式之美》设计模式与范式(结构型-装饰器模式)

2,140 阅读5分钟

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的继承树形图:

image.png

两个变化维度:字节流 or 字符流输入流 or 输出流,组合出四个子类:InputStreamOutputStreamReaderWriter,针对不同的读写场景,从这四个父类的基础上,又扩充出很多子类。

在日常使用时,又涉及到了一个 低级流高级流 的概念,高级流不能直接使用,需要传入低级流,裹好几层,如:

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提供默认实现,以此减少这些重复代码。