装饰(Decorator)模式又称包装(Wrapper)模式。装饰模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案。
我们通过日常使用的手机换代来解释什么是装饰模式。手机总体来看存在三个大的版本,分别是"黑白屏时代"、"半智能时代(即NOKIA的天下)"和"只能手机(即Android、IOS)",如下图:
上图中,手机的黑白屏就是装饰模式的基础构建,其他代的手机都是在黑白屏手机基础上进行增强的。如下:
在黑屏手机时代,手机只能发短信和打电话;
在半智能手机时代,手机不仅可以发短信、打电话,而且可以浏览网页,通过WAP;
在智能手机时代,手机除了上面的功能外,可以完全当作一个PC电脑。功能非常丰富;
如果将上面的手机实例使用Java进行描述,UML类图如下:
上图中,NoopsychePhone和HalfNoopsychePhone均是在BlackAndWhitePhone基础上进行扩展加强功能。其中:
Phone:定义手机的基础功能接口,在Java中使用接口或者抽象类担任。
BlackAndWhitePhone:黑白屏手机实现
Decorator:手机功能扩展的基础类,所有扩展新功能的手机均需要继承该类,该类同时需要引用一个Phone对象。
HalfNoopsychePhone:半智能手机实现
NoopsychePhone:智能手机实现
装饰模式使用原来被装饰的类的一个子类的实例,把客户端的调用委派到被装饰类。装饰模式的关键在于这种扩展是完全透明的。类图如下:
在装饰模式中的各个角色有:
抽象构件(Component)角色:规范准备接收附加责任的对象。在Java中使用接口担任;
具体构建(Concrete Component)角色:将要接收附加责任的类;
装饰(Decorator)角色:持有一个构建(Component)对象的实例,并定义一个与抽象构件接口一致的接口;
具体装饰(Concrete Decorator)角色:负责给构件对象"贴上"附加功能;
Java代码:
// 抽象构件角色 public interface Component { public void sampleOperation(); } // 具体构件角色,将要接收附加功能的基础类 public class ConcreteComponent implements Component { public void sampleOperation() { System.out.println(this.getClass().getName() + " sampleOperation()"); } } // 装饰角色,该类提供了与抽象构件(待装饰类)一样的接口 public class Decorator implements Component { private Component component; public Decorator(Component component) { this.component = component; } public Decorator() {} public void sampleOperation() { component.sampleOperation(); } } // 具体装饰角色(提供一个目标构件没有的功能,或者增强原有的功能) public class ConcreteDecorator extends Decorator { private static final String NAME = ConcreteDecorator.class.getName(); public ConcreteDecorator() {} public ConcreteDecorator(Component component) { super(component); } @Override public void sampleOperation() { System.out.println("[*] " + NAME + " => 开始包装"); super.sampleOperation(); System.out.println("[*] " + NAME + " => 包装结束"); } } // 客户端 public class Client { public static void main(String[] args) { Decorator decorator = new ConcreteDecorator(new ConcreteComponent()); decorator.sampleOperation(); } }
装饰模式在Java IO中得到了淋漓精致的使用。Java中的IO框架使用了装饰模式,下面我们以输入流InputStream为例。类图如下:
上图中是Java IO中输入流InputStream的部分类图,详解如下:
InputStream 接口定义了装饰模式抽象构建的接口列表;
FileInputStream 类是装饰模式的具体构建;
FilterInputStream 类是装饰模式装饰角色的具体实现,其他具体装饰角色均继承自该角色;
BufferedInputStream 类是具体装饰角色的具体实现,提供用来了使用缓存读取文件的接口;
DataInputStream 类是具体装饰角色的具体实现,提供获取int、float、double等基础数据的接口;