装饰者模式

来源:互联网 发布:稀疏编码算法 详解 编辑:程序博客网 时间:2024/06/06 12:44

装饰者模式


纸上得来终觉浅,绝知此事要躬行 — 陆游

任何一个知识点,如果只是了解,没有深究并实践,后面又一直没有用到,那么被遗忘的可能性就很大,前期的时间和精力投入,不能说完全白费,但起码没有达到期望的结果。后期可能需要重复的时间投入,才能把它拾起来。所以呢,咱们就第一次把事情做好^_^。

今天我们聊聊设计模式中的装饰者模式。

什么是装饰者模式


动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。[DP]

比较官方的话,悟了半天也没悟出个所以然--|||。让我们先看看先贤们掉过的坑,获取些启发( ^^ )。

故事开场:

生成子类的模式:
果农老板老李种了些水果,每次都要进城去卖,进城都要走十多里路。一来二去感觉太麻烦,于是就找到程序员小菜提了一个需求,“我想要一个能通信的设备,能直接给超市老板说话,谈生意”。小菜一听,这个简单,三下五除二写了个类A,做了个呼叫机给了老李。老李这下乐呵了,不用每次都进城了,直接通过呼叫机谈。

过了一段时间,菜农老毛又来找小菜(小菜不种菜。。。是个程序员),“小菜啊,听说你给老李做了个呼叫机,挺好使的,给我也来个。不过我这个需要增加个功能。能不能把我要说的话,直接用文字发过去。蔡老板应酬多,呼叫经常接不通。”小菜一想,这个简单,直接写个类B继承类A,再增加个发短信的功能就成。呼呼地小菜就交货了,老蔡为了感谢小菜,还送了抱了两个大白菜给小菜。

没过几天,司机老刘又找上了小菜,“小菜啊,听说你给老毛做的机子不错啊,我也想来一个。除了能打电话、发短信之外,我还想听歌,每次跑长途,都要个把小时,太无聊了,听听歌提提神么”。小菜一想,这个也不难,写个类C继承类B,再增加个听歌功能就行了。没过多久,小菜就做出来了。老刘拿到货很满意地离开了。
生成子类模式

生成子类的模式的不灵活性:

通过做这几个需求,小菜的名声也渐渐地大了,来找他谈需求的人也越来越多。

  • “我想在打电话的基础上要个发语音的功能”
  • “我想要个听音乐看视频的功能”
  • “我想要个发短信和照相的功能”

增加D类,增加E、F。。。
我们发现,针对D,E,F,如果继续使用继承生成子类的方式,就很不灵活。基础模块的功能,我们可以任意组合,生成我们需要的功能。采用生成子类的方式,很容易导致类爆炸——生成很多的子类,因为我们可以有很多种组合,各个类之间强耦合。如果我们想修改打电话的功能,比如说将移动网有3G切换到4G,你不知道会影响多少类,因为有太多类继承于打电话(类A)。如果有些地方没有4G网络覆盖,只有3G网络,那电话就打不通喽。

这里写图片描述

装饰者模式登场:

让我们再来分析下它的定义:动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。

“动态地”:怎么理解这个”动态地”,我们可以给一个对象增加一个功能,也可以将这个增加的功能去除,这就是”动态地”;
“额外的职责”:比如机器能打电话,我们想给它增加一个发信息的功能和听音乐的功能,发信息的功能和听音乐的功能,相对于基础功能打电话,就是额外的职责。
“比子类更为灵活”:针对各个功能模块,我们可以自由组合的场景,如上类D,E,F,采用子类(也就是继承父类)是不是就很不灵活。

装饰者模式UML图


这里写图片描述

装饰者模式实例(Java I/O)


对比装饰者模式的UML图,发现了吧。FilterInputStream就是装饰者。
这里写图片描述

InputStream-IO输入类,那我们一般从哪些渠道获得数据呢?
这里写图片描述
现在需求来了,客户抱怨I/O读数据太慢了。我们想了一个办法,先创建一个Buffer,每次先去Buffer里去取,没有的话再去磁盘上去取。还有Java I/O每次提供给客户的都是byte,还需要客户自己将byte转换成int,String等数据类型。如果让我们去实现这个需求,该怎么处理呢?各位看官不妨想想。。。
5种数据来源(文件/字节数组/StringBuffer/其他线程/已经序列化的对象),每个都有3中可能(实现Buffer/实现数据转化/实现Buffer和数据转化),总共需要新增3 * 5 = 15 个类。想想就恐怖。。。

而采用装饰者模式后,我们只需要增加两个类:
这里写图片描述
上代码:

  • 需要Buffer的话,就穿个BufferedInputStream的外套
  • 需要字节转类型的时候,就穿个DataInputStream的外套
    动态地给InputStream增加一些额外的职责,是不是比增加子类灵活多了呢^_^
package decorate;import java.io.BufferedInputStream;  import java.io.DataInputStream;import java.io.File;  import java.io.FileInputStream;  import java.io.InputStream;    public class Test {      public static void main(String[] args) {          try {              InputStream in = new FileInputStream(new File("c:\\kankan\\test.txt"));              BufferedInputStream bf = new BufferedInputStream(in);              DataInputStream data = new DataInputStream(bf);              String line;            while ((line = data.readLine()) != null) {                 System.out.println(line);            }            data.close();          } catch (Exception e) {              e.printStackTrace();          }      }  }

这里写图片描述
扫码关注我的订阅号”Java技术博文”,更多精彩内容等你来瞧!