定义
装饰器设计模式顾名思义就是装饰某个对象,让一个功能单一的对象拥有一些其他的功能,这些功能的添加是动态的。用户可以随意的扩展原有对象的功能。一方面代替了继承,相对于继承带来的功能扩展,装饰器模式可以理解为动态的扩展,用户需要什么就扩展什么功能,非常灵活,而继承带来的缺点就是不方便修改,是静态的扩展。由于他的方便也带来了缺点,在装饰的过程中,其实创建了很多的对象占据内存资源,这些对象大多都很相似,排查错误也是有很大困难。
比如有一根电线,现在只有铜丝,只不过现在只能导电,我们在他的外面加一层绝缘体比如橡胶,这样他在导电的同时还安全了,外部具有绝缘的功能。
场景
上小学的时候学习成绩非常的差,班级上40多个同学,我基本上都是在排名45名以后,按照老师给我的定义就是“不是读书的料”,但是我老爸管的很严格,明知道我不是这块料,还是赶鸭子上架,每次考试完毕我都是战战兢兢的,“竹笋炒肉”是肯定少不了的,能少点就少点吧,肉可是自己的呀。四年级期末考试考完,学校出来个很损的招儿(这招儿现在很流行的),打印出成绩单,要家长签字,然后才能上五年级,我那个恐惧呀,不过也就是几秒钟的时间,玩起来什么都忘记了。
我们先看看这个成绩单的类图:
成绩单的抽象类,然后有一个四年级的成绩单实现类,先看抽象类:
package com.ssy.wlj.decorator2; /** * 成绩单的抽象类 * @author Administrator * @since 2019/05/21 * */ public abstract class SchoolReport { // 成绩单主要展示的就是你的成绩情况 public abstract void report(); // 成绩单要家长签字,这个是最要命的 public abstract void sign(String name); }
然后看我们的实现类 FouthGradeSchoolReport:
package com.ssy.wlj.decorator2; /** * 四年级的成绩单 * @author Administrator * @since 2019/05/21 * */ public class FouthGradeSchoolReport extends SchoolReport { // 我的成绩单 @Override public void report() { // 成绩单的格式是这个样子的 System.out.println("尊敬的xxx家长:"); System.out.println("......"); System.out.println("语文 62 数学 65 体育 98 自然 63"); System.out.println("......"); System.out.println(" 家长签名: "); } // 家长签名 @Override public void sign(String name) { System.out.println("家长签名为:" + name); } }
把这个成绩单给老爸看看,好,我们修改一下类图,成绩单给老爸看:
老爸开始看成绩单,这个成绩单可是最真实的,啥都没有动过,原装,看 Father 类:
package com.ssy.wlj.decorator2; /** * @Description: 老爸看成绩单了 * @since 2019/05/22 */ public class Client { 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 com.ssy.wlj.decorator2; /** * @Description: * 对这个成绩单进行美化 * Sugar这个词太美好了,名词是糖的意思,动词就是美化 * 给你颗糖你还不美去 * @since 2019/05/21 */ public class SugarFouthGradeSchoolReport extends FouthGradeSchoolReport { // 首先要定义你要美化的方法,先给老爸说学校最高成绩 private void reportHighScore() { System.out.println("这次考试语文最高是75,数学是78,自然是80"); } // 在老爸看完成绩单后,我再汇报在班里的排名情况 private void reportSort() { System.out.println("我在班里的排名是38名..."); } // 由于汇报的内容已经发生变更,所以要重写父类 @Override public void report() { this.reportHighScore(); // 先说最高成绩 super.report(); // 然后老爸看成绩单 this.reportSort(); // 然后告诉老爸排名情况 } }
然后 Father 类稍做修改就可以看到美化后的成绩单,看代码如下:
package com.ssy.wlj.decorator2; /** * @Description: 老爸看成绩单了 * @since 2019/05/22 */ public class Client { public static void main(String[] args) { // 成绩单拿过来 SchoolReport sr = new FouthGradeSchoolReport(); // 看成绩单 sr.report(); // 然后老爸一看,很开心,就签名了 sr.sign("老张"); } }
运行结果:
这次考试语文最高是75,数学是78,自然是80
尊敬的xxx家长:
......
语文 62 数学 65 体育 98 自然 63
......
家长签名:
我在班里的排名是38名...
家长签名为:老张
通过继承确实能够解决这个问题,老爸看成绩单很开心,然后就给签字了,但是现实的情况很复杂的,可能老爸听我汇报最高成绩后,就直接乐开花了,直接签名了,后面的排名就没必要了,或者老爸要先听排名情况,那怎么办?继续扩展类?你能扩展多少个类?这还是一个比较简单的场景,一旦需要装饰的条件非常的多,比如20个,你还通过继承来解决,你想想的子类有多少个?你是不是马上就要崩溃了!
好,你也看到通过继承情况确实出现了问题,类爆炸,类的数量激增,光写这些类不累死你才怪,而且还要想想以后维护怎么办,谁愿意接收这么一大堆类的维护?并且在面向对象的设计中,如果超过2层继承,你就应该想想是不是出设计问题了,是不是应该重新找一条道了,这是经验值,不是什么绝对的,继承层次越多你以后的维护成本越多,问题这么多,那怎么办?
如何使用装饰模式解决问题
好办,装饰模式出场来解决这些问题,我们先来看类图:
增加一个抽象类和两个实现类,其中 Decorator 的作用是封装 SchoolReport 类,看源代码:
package com.ssy.wlj.decorator2; /** * 装饰类,我要把我的成绩单装饰一下 * @author Administrator * @since 2019/05/21 * */ public class Decorator extends SchoolReport { // 首先我要知道是哪个成绩单 private SchoolReport sr; // 构造函数,传递成绩单过来 public Decorator(SchoolReport sr) { this.sr = sr; } // 成绩单还是要被看到的 @Override public void report() { this.sr.report(); } // 看完毕还是要签名 @Override public void sign(String name) { this.sr.sign(name); } }
Decorator抽象类的目的很简单,就是要让子类来对封装SchoolReport的子类,怎么封装?重写report方法!先看HighScoreDecorator实现类:
package com.ssy.wlj.decorator2; /** * 告诉老爸最高成绩 * @author Administrator * @since 2019/05/21 * */ public class HighScoreDecorator extends Decorator { // 构造函数 public HighScoreDecorator(SchoolReport sr) { super(sr); } // 我要汇报最高成绩 private void reportHighScore() { System.out.println("这次考试语文最高是75,数学是78,自然是80"); } // 最高成绩要在老爸看到我的成绩单之前告诉他,否则等他一看,就要抡起扫帚打我,我哪还有机会说啊 @Override public void report() { this.reportHighScore(); super.report(); } }
重写了report()方法,先调用具体装饰类的装饰方法reportHighScore(),然后再调用具体构件的方法,我们再来看怎么回报学校排序情况SortDecorator代码:
package com.ssy.wlj.decorator2; /** * 学校排序情况 * @author Administrator * @since 2019/05/21 * */ public class SortDecorator extends Decorator { // 构造函数 public SortDecorator(SchoolReport sr) { super(sr); } // 告诉老爸排名情况 private void reportSort() { System.out.println("我在班里的排名是38名..."); } // 老爸看完成绩单后再告诉他,加强作用 @Override public void report() { super.report(); this.reportSort(); } }
然后看看我老爸怎么看成绩单的:
package com.ssy.wlj.decorator2; /** * 看成绩单 * @author Administrator * @since 2019/05/21 * */ public class FatherClient { public static void main(String[] args) { // 成绩单拿过来,加了最高分说明的成绩单/加了成绩排名的说明 SchoolReport sr = new SortDecorator(new HighScoreDecorator(new FouthGradeSchoolReport())); // 看成绩单 sr.report(); // 签名 sr.sign("老张"); } }
老爸一看成绩单,听我这么一说,非常开心,儿子有进步呀,从40多名进步到30多名,进步很大,躲过了一顿海扁。
这就是装饰模式,装饰模式的通用类图如下:
编写通用组件接口代码:
package com.ssy.wlj.decorator; /** * 组件接口 * @author Administrator * @since 2019/05/22 * */ public interface Component { void doSomething(); }
编写通用组件实现类:
package com.ssy.wlj.decorator; /** * 组件实现类 * @author Administrator * @since 2019/05/22 * */ public class ConcreteComponent implements Component{ @Override public void doSomething() { System.out.println("功能A"); } }
编写组件装饰类:
package com.ssy.wlj.decorator; /** * 装饰组件 * @author Administrator * */ public class Decorator implements Component { private Component component; public Decorator(Component component) { this.component = component; } @Override public void doSomething() { component.doSomething(); } }
第一个装饰者:
package com.ssy.wlj.decorator; /** * 装饰者1 * @author Administrator * @since 2019/05/22 * */ public class ConcreteDecorator1 extends Decorator { public ConcreteDecorator1(Component component) { super(component); } @Override public void doSomething() { super.doSomething(); this.doAnotherthing(); } private void doAnotherthing() { System.out.println("功能B"); } }
第二个装饰者:
package com.ssy.wlj.decorator; /** * 装饰者2 * @author Administrator * */ public class ConcreteDecorator2 extends Decorator { public ConcreteDecorator2(Component component) { super(component); } @Override public void doSomething() { super.doSomething(); this.doAnotherthing(); } private void doAnotherthing() { System.out.println("功能C"); } }
客户端调用:
package com.ssy.wlj.decorator; /** * 客户端调用 * @author Administrator * @since 2019/05/22 * */ public class Client { public static void main(String[] args) { Component component = new ConcreteDecorator2(new ConcreteDecorator1(new ConcreteComponent())); component.doSomething(); } }
运行结果:
功能A
功能B
功能C