装饰模式(Decorator Pattern)也称为包装模式(Wrapper Pattern),使用一种对客户端透明的方式来动态地扩展对象的功能,同时它也是继承关系的一种替代方案之一。在现实生活中你也可以看见很多装饰模式的例子,或者可以大胆地说装饰模式无处不在,就拿人来说,人需要各式各样的衣着,不管你穿着怎样,但是,对于个人的本质来说是不变的,充其量只是在外面披上一层遮羞物而已,这就是装饰模式,装饰物也许各不相同但是装饰的对象本质是不变的。
1 装饰模式 UML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 public abstract class Component { public abstract void operate () ; } public class ConcreteComponent extends Component { @Override public void operate () { } } public abstract class Decorator extends Component { private Component component; public Decorator (Component component) { this .component = component; } @Override public void operate () { component.operate(); } } public class ConcreteDecoratorA extends Decorator { public ConcreteDecoratorA (Component component) { super (component); } @Override public void operate () { operateA(); super .operate(); operateB(); } public void operateA () { } public void operateB () { } } Component component = new ConcreteComponent(); Decorator decorator = new ConcreteDecoratorA(component); decorator.operate();
2 简单实现 开头说过人需要各式各样的衣着,不管你穿着怎样,但是,对于个人的本质来说是不变的,充其量只是在外面披上一层遮羞物而已,这就是装饰模式。那就用穿衣举例。
1 2 3 public abstract class Person { public abstract void dressed () ; }
这个类就是抽象类, 被装饰的原始对象,然后我们需要一个基础实现类
1 2 3 4 5 6 public class Boy extends Person { @Override public void dressed () { System.out.println("穿平角内裤" ); } }
这里是一个正常男生,实现了最基础的方法,当然你可以新增加一个屌丝男士,穿个丁字的 ~。然后我们需要对这个男生进行包装!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 public class PersonCloth extends Person { protected Person mPerson; public PersonCloth (Person mPerson) { this .mPerson = mPerson; } @Override public void dressed () { mPerson.dressed(); } } public class ExpensiveCloth extends PersonCloth { public ExpensiveCloth (Person mPerson) { super (mPerson); } private void dressShirt () { System.out.println("穿件短袖" ); } private void dressLeather () { System.out.println("穿件皮衣" ); } private void dressJean () { System.out.println("穿条牛仔裤" ); } @Override public void dressed () { super .dressed(); dressShirt(); dressLeather(); dressJean(); } } public class CheapCloth extends PersonCloth { public CheapCloth (Person mPerson) { super (mPerson); } private void dressShorts () { System.out.println("穿个短裤" ); } @Override public void dressed () { super .dressed(); dressShorts(); } }
这是两种不同价位的包装(装饰),两者都是为原本 Boy 类中的 dressed 方法提供功能扩展,不过这种扩展并非是直接修改原有的方法逻辑或结构,更恰当地说,仅仅是在另一个类中将原有方法和新逻辑进行封装整合而已。
1 2 3 4 5 6 7 Person person = new Boy(); PersonCloth clothCheap = new CheapCloth(person); clothCheap.dressed(); PersonCloth clothExpensive = new ExpensiveCloth(person); clothExpensive.dressed();
3 Java 中的装饰模式(Java IO)
在初学 Java 的时候,曾经对 Java IO 的一些用法产生过很大疑惑。我们打开文件 test.txt,从中读取数据。其中,InputStream 是一个抽象类,FileInputStream 是专门用来读取文件流的子类。BufferedInputStream 是一个支持带缓存功能的数据读取类,可以提高数据读取的效率。
1 2 3 4 5 6 InputStream in = new FileInputStream("/test.txt" ); InputStream bin = new BufferedInputStream(in); byte [] data = new byte [128 ];while (bin.read(data) != -1 ) { }
这里的装饰模式有个特殊的使用场景(之前的例子代码上也可以,只是场景不合适),装饰器类和原始类继承同样的父类,这样我们可以对原始类 “嵌套” 多个装饰器类。
对 FileInputStream 嵌套了两个装饰器类:BufferedInputStream 和 DataInputStream,让它既支持缓存读取,又支持按照基本数据类型来读取数据。
1 2 3 4 InputStream in = new FileInputStream("/test.txt" ); InputStream bin = new BufferedInputStream(in); DataInputStream din = new DataInputStream(bin); int data = din.readInt();
其实 BufferedInputStream 和 DataInputStream 继承自 FilterInputStream,Java IO 抽象出的一个装饰器父类,他就类似于 抽象装饰者(PersonCloth) ,因为这两个装饰类只是对部分方法进行装饰,还有一部分方法没必要重写 FilterInputStream 就是统一重写方法,避免代码重复。
具体可以看 JDK 源码,和上面例子基本类似。
4 Android 源码中的装饰模式(Context) 理解Android Context
感谢
设计模式之美
《Android 源码设计模式解析与实战》
以及上文中的链接