定义

装饰器设计模式顾名思义就是装饰某个对象,让一个功能单一的对象拥有一些其他的功能,这些功能的添加是动态的。用户可以随意的扩展原有对象的功能。一方面代替了继承,相对于继承带来的功能扩展,装饰器模式可以理解为动态的扩展,用户需要什么就扩展什么功能,非常灵活,而继承带来的缺点就是不方便修改,是静态的扩展。由于他的方便也带来了缺点,在装饰的过程中,其实创建了很多的对象占据内存资源,这些对象大多都很相似,排查错误也是有很大困难。
比如有一根电线,现在只有铜丝,只不过现在只能导电,我们在他的外面加一层绝缘体比如橡胶,这样他在导电的同时还安全了,外部具有绝缘的功能。

场景
上小学的时候学习成绩非常的差,班级上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

 

 

posted on 2019-05-22 09:34  happy_2010  阅读(174)  评论(0编辑  收藏  举报