以下内容出自:<<24种设计模式介绍与6大设计原则>>

  Ladies and gentlemen,May I get your attention,Please?,Now I’m going to talk about decorator
pattern.装饰模式在中国使用的那实在是多,中国的文化是中庸文化,说话或做事情都不能太直接,需要
有技巧的,比如说话吧,你要批评一个人,你不能一上来就说你这个做的不对,那个做的不对,你要先肯
定他的成绩,表扬一下优点,然后再指出瑕疵,指出错误的地方,最后再来个激励,你修改了这些缺点后
有那些好处,比如你能带更多的小兵,到个小头目等等,否则你一上来就是一顿批评,你瞅瞅看,肯定是
不服气,顶撞甚至是直接“此处不养爷,自有养爷处”开溜哇。这是说话,那做事情也有很多,在山寨产
品流行之前,假货很是比较盛行的,我在2002 年买了个手机,当时老板吹的是天花乱坠,承诺这个手机是
最新的,我看着也像,壳子是崭新的,包装是崭新的,没有任何瑕疵,就是比正品便宜了一大截,然后我
买了,缺钱哪,用来3 个月,坏了,一送修,检查,说这是个新壳装旧机,我晕!拿一个旧手机的线路板,
找个新的外壳、屏幕、包装就成了新手机,装饰模式害人不浅呀!
我们不说不开心的事情,今天举一个什么例子呢?就说说我上小学的的糗事吧。我上小学的时候学习
成绩非常的差,班级上40 多个同学,我基本上都是在排名45 名以后,按照老师给我的定义就是“不是读
书的料”,但是我老爸管的很严格,明知道我不是这块料,还是往赶鸭子上架,每次考试完毕我都是战战兢
兢的,“竹笋炒肉”是肯定少不了的,能少点就少点吧,肉可是自己的呀。四年级期末考试考完,学校出来
个很损的招儿(这招儿现在很流行的),打印出成绩单,要家长签字,然后才能上五年级,我那个恐惧呀,
不过也就是几秒钟的时间,玩起来什么都忘记了。
我们先看看这个成绩单的类图:

成绩单的抽象类,然后有一个四年级的成绩单实现类,先看抽象类:

package cn.mjorcen.decorator_pattern.service;/*** @author cbf4Life cbf4life@126.com I'm glad to share my knowledge with you*         all. 成绩单的抽象类*/
public abstract class SchoolReport {// 成绩单的主要展示的就是你的成绩情况public abstract void report();// 成绩单要家长签字,这个是最要命的public abstract void sign(String name);
}

然后看我们的实现类FouthGradSchoolReport:

package cn.mjorcen.decorator_pattern.service.impl;import cn.mjorcen.decorator_pattern.service.SchoolReport;/*** @author cbf4Life cbf4life@126.com I'm glad to share my knowledge with you*         all. 您的设计模式 第 128 页 四年级的成绩单,这个是我们学校第一次实施,以前没有干过 这种“缺德”事。*/
public class FouthGradeSchoolReport extends SchoolReport {// 我的成绩单public void report() {// 成绩单的格式是这个样子的System.out.println("尊敬的XXX家长:");System.out.println(" ......");System.out.println(" 语文 62 数学65 体育 98 自然 63");System.out.println(" .......");System.out.println(" 家长签名: ");}// 家长签名public void sign(String name) {System.out.println("家长签名为:" + name);}}

  成绩单出来,你别看什么62,65 之类的成绩,你要知道在小学低于90 分基本上就是中下等了,唉,
爱学习的人太多了!怎么着,那我把这个成绩单给老爸看看?好,我们修改一下类图,成绩单给老爸看:

  老爸开始看成绩单,这个成绩单可是最真实的,啥都没有动过,原装,看Father 类:

package cn.mjorcen.decorator_pattern.service.impl;import cn.mjorcen.decorator_pattern.service.SchoolReport;/*** @author cbf4Life cbf4life@126.com I'm glad to share my knowledge with you*         all. 老爸看成绩单了*/
public class Father {public static void main(String[] args) {// 成绩单拿过来SchoolReport sr = new FouthGradeSchoolReport();// 看成绩单
        sr.report();// 签名?休想!
    }
}

运行结果如下:

尊敬的XXX家长:
......
语文 62 数学65 体育 98 自然 63
.......
家长签名:

  就这成绩还要我签字?!老爸就开始找笤帚,我的屁股已经做好了准备,肌肉要绷紧,要不那个太疼了!哈
哈,幸运的是,这个不是当时的真实情况,我没有直接把成绩单交给老爸,而是在交给他之前做了点技术工作,
我要把成绩单封装一下,封装分类两步走:
 

第一步:跟老爸说各个科目的最高分,语文最高是75,数学是78,自然是80,然老爸觉的我成绩与最高分
数相差不多,这个是实情,但是不知道是什么原因,反正期末考试都考的不怎么样,但是基本上都集中在70 分
以上,我这60 多分基本上还是垫底的角色;

第二步:在老爸看成绩单后,告诉他我是排名第38 名,全班,这个也是实情,为啥呢?有将近十个同学退
学了!这个情况我是不说的。不知道是不是当时第一次发成绩单,学校没有考虑清楚,没有写上总共有多少同学,
排名第几名等等,反正是被我钻了个空子。
那修饰是说完了,我们看看类图如何修改:

  我想这是你最容易想到的类图,通过直接增加了一个子类,重写report 方法,很容易的解决了这个问题,
是不是这样?是的,确实是,确实是一个很好的办法,我们来看具体的实现:

package cn.mjorcen.decorator_pattern.service.impl;/*** @author cbf4Life cbf4life@126.com I'm glad to share my knowledge with you*         all. 对这个成绩单进行美化 Sugar这个词太好了,名词是糖的意思,动词就是美化 给你颗糖你还不美去*/
public class SugarFouthGradeSchoolReport extends FouthGradeSchoolReport {// 首先要定义你要美化的方法,先给老爸说学校最高成绩private void reportHighScore() {System.out.println("这次考试语文最高是75,数学是78,自然是80");}// 在老爸看完毕成绩单后,我再汇报学校的排名情况private void reportSort() {System.out.println("我是排名第38名...");}// 由于汇报的内容已经发生变更,那所以要重写父类
    @Overridepublic void report() {this.reportHighScore(); // 先说最高成绩super.report(); // 然后老爸看成绩单this.reportSort(); // 然后告诉老爸学习学校排名
    }
}

然后 Father 类稍做修改就可以看到美化后的成绩单,看代码如下:

package cn.mjorcen.decorator_pattern.service.impl;import cn.mjorcen.decorator_pattern.service.SchoolReport;/*** @author cbf4Life cbf4life@126.com I'm glad to share my knowledge with you*         all. 老爸看成绩单了*/
public class Father {public static void main(String[] args) {// 美化过的成绩单拿过来SchoolReport sr = new SugarFouthGradeSchoolReport();// 看成绩单
        sr.report();// 然后老爸,一看,很开心,就签名了sr.sign("老三"); // 我叫小三,老爸当然叫老三
    }
}

运行结果如下:

这次考试语文最高是75,数学是78,自然是80
尊敬的XXX家长:
......
语文 62 数学65 体育 98 自然 63
.......
家长签名:
我是排名第38名...
家长签名为:老三

  通过继承确实能够解决这个问题,老爸看成绩单很开心,然后就给签字了,但是现实的情况很复杂的,可能
老爸听我汇报最高成绩后,就直接乐开花了,直接签名了,后面的排名就没必要了,或者老爸要先听排名情况,
那怎么办?继续扩展类?你能扩展多少个类?这还是一个比较简单的场景,一旦需要装饰的条件非常的多,比如
20 个,你还通过继承来解决,你想想的子类有多少个?你是不是马上就要崩溃了!
  好,你也看到通过继承情况确实出现了问题,类爆炸,类的数量激增,光写这些类不累死你才怪,而且还要
想想以后维护怎么办,谁愿意接收这么一大堆类的维护哪?并且在面向对象的设计中,如果超过2 层继承,你就
应该想想是不是出设计问题了,是不是应该重新找一条道了,这是经验值,不是什么绝对的,继承层次越多你以
后的维护成本越多,问题这么多,那怎么办?好办,装饰模式出场来解决这些问题,我们先来看类图:

增加一个抽象类和两个实现类,其中Decorator 的作用是封装SchoolReport 类,看源代码:

package cn.mjorcen.decorator_pattern.service.impl;import cn.mjorcen.decorator_pattern.service.SchoolReport;/*** @author cbf4Life cbf4life@126.com I'm glad to share my knowledge with you*         all. 装饰类,我要把我的成绩单装饰一下*/
public abstract class Decorator extends SchoolReport {// 首先我要知道是那个成绩单private SchoolReport sr;// 构造函数,传递成绩单过来public Decorator(SchoolReport sr) {this.sr = sr;}// 成绩单还是要被看到的public void report() {this.sr.report();}// 看完毕还是要签名的public void sign(String name) {this.sr.sign(name);}
}

Decorator 抽象类的目的很简单,就是要让子类来对封装SchoolReport 的子类,怎么封装?重写
report 方法!先看HighScoreDecorator 实现类:

package cn.mjorcen.decorator_pattern.service.impl;import cn.mjorcen.decorator_pattern.service.SchoolReport;/*** @author cbf4Life cbf4life@126.com I'm glad to share my knowledge with you*         all. 我要把我学校的最高成绩告诉老爸*/
public class HighScoreDecorator extends Decorator {// 构造函数public HighScoreDecorator(SchoolReport sr) {super(sr);}// 我要汇报最高成绩private void reportHighScore() {System.out.println("这次考试语文最高是75,数学是78,自然是80");}// 最高成绩我要做老爸看成绩单前告诉他,否则等他一看,就抡起笤帚有揍我,我那还有机会说呀
    @Overridepublic void report() {this.reportHighScore();super.report();}
}

重写了 report 方法,先调用具体装饰类的装饰方法reportHighScore,然后再调用具体构件的方法,

我们再来看怎么回报学校排序情况SortDecorator 代码:

package cn.mjorcen.decorator_pattern.service.impl;import cn.mjorcen.decorator_pattern.service.SchoolReport;/*** @author cbf4Life cbf4life@126.com I'm glad to share my knowledge with you*         all. 学校排名的情况汇报*/
public class SortDecorator extends Decorator {// 构造函数public SortDecorator(SchoolReport sr) {super(sr);}// 告诉老爸学校的排名情况private void reportSort() {System.out.println("我是排名第38名...");}// 老爸看完成绩单后再告诉他,加强作用
    @Overridepublic void report() {super.report();this.reportSort();}
}

然后看看我老爸怎么看成绩单的:

package cn.mjorcen.decorator_pattern.service.impl;import cn.mjorcen.decorator_pattern.service.SchoolReport;/*** @author cbf4Life cbf4life@126.com I'm glad to share my knowledge with you*         all. 老爸看成绩单了*/
public class Father {public static void main(String[] args) {// 成绩单拿过来
        SchoolReport sr;sr = new FouthGradeSchoolReport(); // 原装的成绩单// 加了最高分说明的成绩单sr = new HighScoreDecorator(sr);// 又加了成绩排名的说明sr = new SortDecorator(sr);// 看成绩单
        sr.report();// 然后老爸,一看,很开心,就签名了sr.sign("老三"); // 我叫小三,老爸当然叫老三
    }
}

老爸一看成绩单,听我这么一说,非常开心,儿子有进步呀,从40 多名进步到30 多名,进步很大,
躲过了一顿海扁。
这就是装饰模式,装饰模式的通用类图如下:

  看类图,Component 是一个接口或者是抽象类,就是定义我们最核心的对象,也就是最原始的对象,比如上面的成绩单,记住在装饰模式中,必然有一个被提取出来最核心、最原始、最基本的接口或抽象类,就是Component。

  ConcreteComponent 这个事最核心、最原始、最基本的接口或抽象类的实现,你要装饰的就是这个东东。Decorator 一般是一个抽象类,做什么用呢?实现接口或者抽象方法,它里面可不一定有抽象的方法呀,在它的属性里必然有一个private 变量指向Component。

  ConcreteDecoratorA 和ConcreteDecoratorB 是两个具体的装饰类,你要把你最核心的、最原始的、最基本的东西装饰城啥东西,

上面的例子就是把一个比较平庸的成绩单装饰成家长认可的成绩单。装饰模式是对继承的有力补充,你要知道继承可不是万能的,继承可以解决实际的问题,但是在项目中你要考虑诸如易维护、易扩展、易复用等,而且在一些情况下(比如上面那个成绩单例子)你要是用继承就会增加很多了类,而且灵活性非常的差,那当然维护也不容易了,也就是说装饰模式可以替代继承,解决我们类膨胀的问题,你要知道继承是静态的给类增加功能,而装饰模式则是动态的给增加功能,你看上面的那个例子,我不想要SortDecorator 这层的封装也很简单呀,直接在Father 中去掉就可以了,如果你用继承就必须修改程序。

  装饰模式还有一个非常好的优点,扩展性非常好,在一个项目中,你会有非常多因素考虑不到,特别是业务的变更,时不时的冒出一个需求,特别是提出一个令项目大量延迟的需求时候,那种心情是…,真想骂娘!装饰模式可以给我们很好的帮助,通过装饰模式重新封装一个类,而不是通过继承来完成,简单点说,三个继承关系Father,Son,GrandSon 三个类,我要再Son 类上增强一些功能怎么办?我想你会坚决的顶回去!不允许,对了,为什么呢?你增强的功能是修改Son 类中的方法吗?增加方法吗 ?对GrandSon的影响哪?特别是GrandSon 有多个的情况,你怎么办?这个评估的工作量就是够你受的,所以这个是不允许的,那还是要解决问题的呀,怎么办?通过建立SonDecorator 类来修饰Son,等于说是创建了一个新的类,这个对原有程序没有变更,通过扩充很好的完成了这次变更。

第 13 章 装饰模式【Decorator Pattern】相关推荐

  1. 二十四种设计模式:装饰模式(Decorator Pattern)

    装饰模式(Decorator Pattern) 介绍 动态地给一个对象添加一些额外的职责.就扩展功能而言,它比生成子类方式更为灵活. 示例 有一个Message实体类,某个对象对它的操作有Insert ...

  2. 设计模式系列之装饰模式(Decorator Pattern)

    装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构.这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装.这种模式创建了一个装饰类,用来包装原 ...

  3. 七、装饰模式(Decorator Pattern)

    一.介绍 意图:动态地给一个对象添加一些额外的职责.就增加功能来说,装饰器模式相比生成子类更为灵活. 主要解决:使用继承实现类的功能的扩展,有时子类会过多的问题. 应用实例: 1.一幅照片,将它放入玻 ...

  4. 设计模式-装饰模式(Decorator Pattern)

    Attach additional responsibilities to an object dynamically keeping the same interface.Decorators pr ...

  5. 使用C# (.NET Core) 实现装饰模式 (Decorator Pattern) 并介绍 .NET/Core的Stream

    该文章综合了几本书的内容. 某咖啡店项目的解决方案 某咖啡店供应咖啡, 客户买咖啡的时候可以添加若干调味料, 最后要求算出总价钱. Beverage是所有咖啡饮料的抽象类, 里面的cost方法是抽象的 ...

  6. 23装饰模式(Decorator Pattern)

    子类复子类,子类何其多 假如我们需要为游戏中开发一种坦克,除了各种不同型号的坦克外,我们还希望在不同场合中为其增加以下一种或多种功能;比如红外线夜视功能,比如水陆两栖功能,比如卫星定位功能等等. 按类 ...

  7. 基于东北F4的设计模式情景剧——第一幕 装饰模式(Decorator Pattern)

    第一场 难题未解 布景:铁岭,晴天,午后,风.在一幢还算气派的写字楼的三层外墙上,挂着一条红色横幅,上面用歪歪扭扭的毛笔字写着"东北F4软件外包工作室".大风中,那早已褪色的条幅剧 ...

  8. 装饰模式 Decorator Pattern

    java设计模式_装饰者模式(带例子) Decorator模式(装饰者)    Java深入到一定程度,就不可避免的碰到设计模式这一概念,了解设计模式,将使自己对java中的接口或抽象类应用有更深的理 ...

  9. 设计模式(13):结构型-装饰模式(Decorator)

    设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了可重用代码.让代码更容易被他人理解.保证代码可靠性. 毫无疑问,设计模式于 ...

  10. decorator java_java_详解java装饰模式(Decorator Pattern),一、装饰器模式(Decorator Patter - phpStudy...

    详解java装饰模式(Decorator Pattern) 一.装饰器模式(Decorator Pattern) 允许向一个现有的对象添加新的功能,同时又不改变其结构.这种类型的设计模式属于结构型模式 ...

最新文章

  1. Redis系列(四)-低成本高可用方案设计
  2. 如何调整金格电子章服务器印章_如何利用OA系统进行电子公章、红头文件及打印的管理...
  3. 为什么MySQL数据库要用B+树存储索引
  4. 查看linux的用户
  5. 保姆级计算机视觉学习路线
  6. 微信上线「行程查询」服务;钉钉 CEO 回应被打「一星」;Go 1.14 发布 | 极客头条...
  7. 广州山地计算机软件公司,山地企业CA安全认证系统
  8. 基于多线程的Linux聊天室系统设计(C语言实现)
  9. 在Anylogic建立自己的智能体
  10. jpg转pdf怎么转换?jpg转pdf方法
  11. Android 混淆大全一篇就够了
  12. 一篇文带你从0到1了解建站及完成CMS系统编写
  13. 某cpws - ciphertext加密与数据解密
  14. 一字节BCD码转ASCII码的算法及源码
  15. qlabel文本改变信号_改变人类历史的17个方程,神奇!
  16. 骄傲自满的联发科在中高端市场沦落,技术还是不如高通啊
  17. python刷微博转发_一个简单的python刷新浪微博粉丝小程序
  18. 【有问必答】CSDN问答功能测评
  19. AdaBoost AdaRank
  20. 【Matlab多目标优化求解】遗传优化萤火虫算法求解多目标优化问题【含源码 1484期】

热门文章

  1. PHP array(递归)转xml,xml转array
  2. Python百度语音合成
  3. voronoi图代码_在Unity中实时计算Voronoi图
  4. PHP中字符串类型与数值类型混合计算
  5. 全网首发:制作LINUX安装软件包,要处理哪些系统目录和文件(2)
  6. JAVA RSA加密解密代码范例(Base64版)
  7. 管理新语:主管要辅导员工转正
  8. 桃养人,杏害人,樱桃树下埋死人
  9. 进程标识符及fork
  10. 对 /etc/rc.d/init.d 目录的一点理解