理解装饰者模式

来源:互联网 发布:网络存储工程师 编辑:程序博客网 时间:2024/04/30 13:14

引言

  Android面试经常被问到设计模式,而装饰者模式被问到的概率尤其高。也许你在开发中或多或少遇到过类似这样的代码:

BufferedInputStream bis = new BufferedInputStream(new FileInputStream(new File("fileName")));

这是装饰者模式在JDK中的典型应用。设计模式的学习切忌走马观花,求大求全。否则容易理解不深,讲不出所以然,在面试时遭遇尴尬。

定义

  装饰者模式的书面定义是:“动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比生成子类更为灵活。”——语出《Android源码设计模式解析与实战》。装饰者模式的UML类图如下。

UML类图

每一种设计模式都是一个微型的软件系统,设计模式的UML类图是对相应系统的高度概括。下图的装饰者模式类图分为蓝色和绿色两个部分。左上的蓝色部分是被装饰者,右下的绿色部分是装饰者。
这里写图片描述

一些基础

  面向接口编程:将一个业务的定义和实现进行分离。比如人的穿衣这件事,分两个部分来完成。

public interface IPerson{    public void dress();}public class PersonImpl implements IPerson{    @Override    public void dress(){        ...//方法体实现    }    public void fff(){        ...    }    //其他方法}

  第一个是定义一个接口(或者抽象类)IPerson,将人的共通的特性放在里面,而某些特定的特性放在其实现类PersonImpl中。怎么样,是不是很有Java EE 搭建框架的感觉?这样做的缺点是多了一个类,代码增加了,但是大有裨益。一个显而易见的好处是,当你在某处拿到一个IPerson引用时,你的IDE只会提示你IPerson里面的方法,不会提示你PersonImpl中的fff()方法,这在API级别屏蔽了一些细节。

看图说话

  于是我们顺理成章地理解了,最初我们有左上角那两个蓝色的类。一个抽象类Component,一个是这个类的具体实现类ConcreteComponent,它们的本职是operate()。对应于人穿衣这件事就是,一个IPerson接口和PersonImpl类,它们的本职是dress()。如装饰者模式的定义所言,为了给被装饰者增加一些额外的职责,我们需关注装饰者部分,即图中右下蓝色部分。究竟它们是怎样给被装饰者添加额外的职责的,还是类比人穿衣打扮这件事的话,就是究竟是怎样在人穿衣的前提下又增加其他装饰物的?
  由于装饰物有很多种,比如假发、口红、丝巾等等,所以依照面向接口的编程的思想,会抽取出共通的部分,放到基类中,见图中Decorator类。由于我们需要知道装饰者装饰的到底是谁,所以装饰物Decorator需要保存一个对本体的引用;又由于我们希望本体被装饰后不改变其原有的行为,不能披上丝巾就失去了穿裤子的能力,所以饰物Decorator需要继承本体抽取出来的抽象类Component(或者实现Component接口)。详见以下代码:

public abtract 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{    ...    @Override    public void operate(){        operateA1();        super.operate();        operateA12();    }    ...

  结合类图和代码可知,ConcreteComponent是本体,职责是operate(), ConcreteDecoratorA是装饰者之一,ConcreteDecoratorA给ConcreteComponent增加了operateA1()、operateA2()两个额外的职责。但是原有的operate()方法并没有被隐藏或改变,只是其具体实现更丰富了。一个人本来只能穿衣服,后来他可以穿上衣服,并且打上领带拿着公文包。
  听起来很像代理模式?是的,很像很像。只是代理模式是在代理类中实例化被代理者对象,而装饰者模式是在构造函数中传入本体对象作为引用;代理模式重点在隐藏被代理者的具体内容,而装饰者重点在制造出一系列可插拔的装饰物并且不改变本体的行为 ,关注点不同。

装饰者模式的应用

装饰者模式在JDK中的应用

BufferedInputStream bis = new BufferedInputStream(new FileInputStream(new File("fileName")));

  回到引言中提到的代码,BufferedInputStream相当于装饰者模式UML类图中的ConcreteDecoratorA这个角色,而FileInputStream相当于ConcreteComponent角色。Decorator角色的正确理解是:它并不是纯粹的装饰者,它是装饰本体后的对象。按人-衣物的例子来说,Decorator表示穿上某件衣服饰品的人,而不是饰品本身,这和实际生活有细微的差别。实际生活中,装饰品可以单独存在。你穿,或者不穿,衣服饰品就在那,不多不少。而装饰者模式中,装饰者这个角色必须要在构造函数中传入一个本体,即它所装饰的东西,要不然它没有什么实际意义。ConcreteComponent是你,ConcreteDecoratorA是穿上衣服A的你,而不是A那件衣服本身。ConcreteDecoratorA是为“可穿衣服的的东西”量身定做,它可以给你穿,给路人甲穿,甚至给狗穿。但是不能给树穿给天空穿,尽管实际生活中可以。
  

装饰者模式在Android源码中的应用

  当我参照资料画出下面的UML类图,我被自己吓了一跳!我们在Android开发中每天都见到的Activity原来是个装饰者,其装饰的本尊是ContextImpl,那些常用的方法,诸如startActivity,是在ContextImpl中具体实现的。而且不止有Activity,Application和Service都是ContextImpl的装饰者,因此你可以在Application中启动一个Activity,也可以在Service中启动。
这里写图片描述
  Context这个Android开发中的上帝对象,在本文中就不展开论述。
  

1 0