装饰模式:
装饰模式,又称包装模式(Wrapper),其作用在于,动态地为一个类添加职能。
一般来说,为一个类添加额外的功能,第一个想到的应该是使用继承。
在设计时,有这么一条准则:组合优于继承。
装饰模式,就是组合的一种应用,相比继承,它针对类的扩展更具有弹性。
如何理解更具有弹性:
继承而言,子类与父类拥有很高的一致性,这种紧耦合使得继承在面对扩展或者变更时,对现有代码的复用性很弱。
装饰模式中,将需要扩展功能的类,嵌入到另一个,专门负责扩展功能的类中,从而达到解耦的目的。
以上两者,可以独自的扩展,互不影响。
使用场景:
一般而言,遇到一下三种情况时,考虑使用装饰模式:
- 需要扩展一个类的功能,或给一个类增加附加责任。
- 需要动态的给一个对象增加功能,这些功能可以再动态地撤销。
- 需要增加一些基本功能的排列组合而产生的非常大量的功能。
角色分析:
先用一张别人博客中看到的 UML 类图(链接):
装饰模式,主要由以下4部分组成:
Component:抽象构件,抽象类或接口,定义被装饰类的行为。
ConcreteComponent:具体构件,被装饰类。
Decorator:装饰角色,持有被装饰类对象的实例,与其拥有相同的继承/实现关系。
ConcreteDecorator:具体装饰角色,为被装饰类,添加额外的职能。
在一些简单的应用场景中,以上4部分可以依据实际情况,合并一部分。
代码:
1 public interface Component { 2 3 void methodA(); 4 5 void methodB(); 6 7 }
1 public class ConcreteComponent implements Component { 2 3 @Override 4 public void methodA() { 5 System.out.println(this.getClass().getName() + ": MethodA"); 6 } 7 8 @Override 9 public void methodB() { 10 System.out.println(this.getClass().getName() + ": MethodB"); 11 } 12 13 }
1 @AllArgsConstructor 2 public abstract class Decorator implements Component { 3 4 private Component component; 5 6 @Override 7 public void methodA() { 8 component.methodA(); 9 } 10 11 @Override 12 public void methodB() { 13 component.methodB(); 14 } 15 16 }
1 public class ConcreteDecoratorA extends Decorator { 2 3 public ConcreteDecoratorA(Component component) { 4 super(component); 5 } 6 7 public void methodC() { 8 System.out.println(this.getClass().getName() + ": MethodC"); 9 } 10 11 }
1 @Data 2 @EqualsAndHashCode(callSuper = false) 3 public class ConcreteDecoratorB extends Decorator { 4 5 private Object specificatedField; 6 7 public ConcreteDecoratorB(Component component) { 8 super(component); 9 } 10 11 @Override 12 public synchronized void methodA() { 13 System.out.println("Some operation before"); 14 super.methodA(); 15 } 16 17 }
以上代码中:
装饰类A(ConcreteDecoratorA),为对象添加了额外的功能(methodC)。
装饰类B(ConcreteDecoratorB),增加了额外的属性(specificatedField),并且为原有的操作,增加了一些修饰(methodA)。
典型应用:
在 JDK 源码中,对装饰模式的最典型应用,莫过于 IO 流的设计了。
所有 IO 流相关的类,都是从输入(InputStream)/输出(OutputStream)流这两个类扩展而成的。
以下是我截取了 InputStream 结构中的部分设计,绘制的 UML:
不难发现,IO 流中的结构,可以与装饰模式中的角色一一对应:
Component -> InputStream。
ConcreteComponent -> ByteArrayInputStream, FileInputStream。
Decorator -> FilterInputStream。
ConcreteDecorator -> DataInputStream, BufferInputStream, PushbackInputStream。