装饰器模式

装饰器模式通过将对象放入一个装饰器类再将装饰器类放入另一个装饰器类,以此类推形成装饰器包装链,这样就可以在不改变原对象的前提下动态的添加新的行为

概述

先来看一个小例子

定义抽象类

1
2
3
public interface Component {
void operation();
}

定义具体的被装饰类,实现抽象类方法

1
2
3
4
5
6
public class ConcreteComponent implements Component {
@Override
public void operation() {
System.out.println("ConcreteComponent is doing something...");
}
}

定义装饰器抽象类

1
2
3
4
5
6
7
8
9
10
public abstract class Decorator implements Component {
protected Component component;
public Decorator(Component component) {
this.component = component;
}
@Override
public void operation() {
component.operation();
}
}

定义具体的装饰器类,继承抽象装饰器类

1
2
3
4
5
6
7
8
9
10
public class ConcreteDecoratorA extends Decorator {
public ConcreteDecoratorA(Component component) {
super(component);
}
@Override
public void operation() {
super.operation();
System.out.println("ConcreteDecoratorA is adding new behavior...");
}
}
1
2
3
4
5
6
7
8
9
10
public class ConcreteDecoratorA extends Decorator {
public ConcreteDecoratorA(Component component) {
super(component);
}
@Override
public void operation() {
super.operation();
System.out.println("ConcreteDecoratorA is adding new behavior...");
}
}

使用装饰器增强被装饰对象

1
2
3
4
5
6
7
public class Main {
public static void main(String[] args) {
Component component = new ConcreteComponent();
component = new ConcreteDecoratorA(component);
component.operation();
}
}

仔细观察可以看出装饰器模式跟代理模式中静态代理很像,但是从还是有一些区别的

  1. 代理模式的目的是为了控制对对象的访问,它在对象外部提供了一个代理对象来控制原始对象的访问
  2. 装饰器模式目的是动态地增强对象的功能,它在对象的内部通过一种包装器的方式来实现

应用场景

下面从JAVA的IO库为例子理解装饰器

InputStream 是一个抽象类,FileInputStream 是专门用来读取文件流的子类。BufferedInputStream 是一个支持带缓存功能的数据读取类,我们用IO流打开一个文件读取数据的时候,会这样写

1
2
3
4
5
6
InputStream in = new FileInputStream("D:/test.txt");
InputStream bin = new BufferedInputStream(in);
byte[] data = new byte[128];
while (bin.read(data) != -1) {
//...
}

那么为什么我们不使用继承创建一个BufferedFileInputStream类呢,单论创建这一个孙子类我们还能接受,因为继承结构还算简单,但是实际上继承Inputstream的子类很多,如果采用这种继承的方法,我们要让全部子类都派生继承Buffered类吗,那除了缓存的增强外,其他增强类也要再全部添加派生类吗?

这显然不合理,按照这样子组合,类的继承和组合将会十分复杂且极难扩展,这时候,基于装饰器模式的设计方案就很好的解决了这些问题,在设计原则的总结中,我们得出了**”组合优于继承“**的结论,InputStream就是很好的应用了组合代替继承,但是装饰器模式不仅只是简单的组合

  1. 首先原始类和装饰器类继承自同样的父类,这样我们在使用的时候,就可以嵌套多个装饰器类形成装饰器链
  2. 装饰器类是对功能的增强,区别于代理模式,代理模式是增加与原始类无关的功能,而装饰器类附加的是跟原始类相关的增强功能

总结

装饰器模式主要解决继承关系过于复杂的问题,通常是通过组合来替代继承。它主要的作用是给原始类添加增强功能。这也是判断是否该用装饰器模式的一个重要的依据。除此之外,装饰器模式还有一个特点,那就是可以对原始类嵌套使用多个装饰器。为了满足这个应用场景,在设计的时候,装饰器类需要跟原始类继承相同的抽象类或者接口