装饰者模式

装饰者模式


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

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

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

什么是装饰者模式


动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。[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技术博文”,更多精彩内容等你来瞧!

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值