23种设计模式-装饰模式

  1. 罪恶的成绩单

考试成绩单以及成绩排名,大家都懂得,以前上学的时候,这玩意往家里寄是真的要命。成绩单还需要家长签字,那么,我们来模拟将成绩单给家长签字这一情景,如图17-1:


17-1

我们先来看成绩单的抽象类SchoolReport,代码如下:

public abstract class SchoolReport {
    //显示成绩情况
    public abstract void report();
    //要家长签字
    public abstract void sign();
}

有抽象类了,在来看看具体的四年级成绩单FouthGradeSchoolReport,代码如下:

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);
    }
}

成绩单出来62、65之类的在小学基本就是垫底,那么我们把成绩单带回去给家长看,修改一下类图,如图17-2:


17-2

这是原装的成绩单,没有动过的,来看Father类代码如下:

public class Father {
    public static void main(String[] args) {
        SchoolReport sc = new FouthGradeSchoolReport();
        //看成绩单
        sc.report();
        //签名?休想,一顿胖揍
    }
}

这个成绩直接拿出来肯定是找打,我们把成绩单封装一下,封装分为两步来实现:

  • 汇报最高成绩
    跟家长说各个科目的最高分,语文最高75,数学最高78,自然是80;那么家长一看你的成绩和最高分差不多,也还行;实际普遍在70以上,这个60多还是垫底
  • 汇报排名
    全班排第38名,但是不能说参加考试的只有40个人,反正成绩单上没写多少人,相比较之前的40多名还是有进步的,哈哈
    这就是加上了一些修饰,我们看看类图如何修改,如图17-3:


    17-3

    这应该是常规的处理方式了,直接增加一个子类,覆写report方法,我们看具体的实现,代码如下:

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();
    }
}

通过继承确实能够解决这个问题,但是这个只能作为应急的一种方式,如果需要继续加装饰那怎么办,而且装饰条件不是一次就明确的,可能过一段时间加一个,那一直通过继承来处理肯定是不合理的。在面向对象的设计中,如果超过两层,是不是该考虑设计的问题了,这是经验,不是绝对,继承的层次越多以后的维护成本越多,那这么解决这个装饰的问题呢,我们专门定义一批负责装饰的类,然后根据实际情况来决定是否需要进行装饰,类图如17-4:


17-4

增加一个抽象类和两个实现类,其中Decorator的作用是封装SchoolReport类,如果大家还记得代理模式,那么很容易看懂这个类图,装饰类的作用也是一个特殊的代理类,真实的执行者还是被代理的角色FouthGradeSchoolReport,代码如下:

public abstract class Decorator extends SchoolReport {
    //首先得知道是哪个成绩单
    private SchoolReport schoolReport;
    public Decorator(SchoolReport schoolReport){
        this.schoolReport = schoolReport;
    }
    @Override
    public void report(){
        this.schoolReport.report();
    }
    @Override
    public void sign(String name){
        this.schoolReport.sign(name);
    }
}

装饰类还是把动作的执行委托给需要装饰的对象,Decorator抽象类的目的很简单,就是要让子类来封装SchoolReport的子类,先看HighScoreDecorator实现类,代码如下:

public class HighScoreDecorator extends Decorator {

    public HighScoreDecorator(SchoolReport schoolReport) {
        super(schoolReport);
    }
    private void reportHighScore(){
        System.out.println("这次考试语文最高是75,数学是78,自然是80");
    }

    @Override
    public void report() {
        this.reportHighScore();
        super.report();
    }
}

重写了report方法,先调用具体装饰类的装饰方法reportHighScore,然后再调用具体构件的方法,我们再来看怎么汇报学校排序情况SortDecorator代码,代码如下:

public class SortDecorator extends Decorator {
    public SortDecorator(SchoolReport schoolReport) {
        super(schoolReport);
    }
    private void reportSort(){
        System.out.println("我是排名第38名。。。");
    }
    @Override
    public void report(){
        super.report();
        this.reportSort();
    }
}

通过这两个强力的修饰工具,然后就可以拿一份"漂亮"的成绩单出来了,代码如下:

public class Client  {
    public static void main(String[] args) {
        SchoolReport schoolReport;
        schoolReport = new FouthGradeSchoolReport();
        schoolReport = new HighScoreDecorator(schoolReport);
        schoolReport = new SortDecorator(schoolReport);
        schoolReport.report();
        schoolReport.sign("老三");
    }
}

这就是装饰者模式,可以处理一些用继承来处理的问题,但是比继承更加的灵活,但是装饰过多层之后就不太友好了,就像继承过多之后可读性就变的很差了。

  1. 装饰者模式的定义

装饰模式(Decorator Pattern)是一种比较常见的模式,其定义如下:Attach additional responsibilities to an object dynamically keeping the same interface.Decorators provide a flexible alternative to subclassing for extending functionality.(动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比生成子类更为灵活。)
类图17-5:


17-5

在类图中,有四个角色需要说明:

  • Component抽象构件
    Component是一个接口或者是抽象类,就是定义我们最核心的对象,也就是最原始的对象,如上面的成绩单。
    注意 在装饰模式中,必然有一个最基本、最核心、最原始的接口或抽象类充当 Component抽象构件(这个抽象构件应该是描述我们想要装饰的部分
  • ConcreteComponent具体构件
    ConcreteComponent是最核心、最原始、最基本的接口或抽象类的实现,就是我们要装饰的类,就是对应的例子中的FouthGradeSchoolReport
    -Decorator装饰角色
    一般是一个抽象类,实现接口或抽象方法,在他的属性里必然有一个private变量指向Component抽象构件。(装饰者模式的核心,和被装饰者实现或继承相同抽象,然后私有属性指向被装饰者
    -具体装饰角色
    ConcreteDecoratorA和ConcreteDecoratorB是两个具体的装饰类,这个就是用来装饰的了,想从哪个方面哪个角度去装饰,就实现Decorator去写自己的装饰方法就好了。
    我们来看一下装饰者模式的通用代码:
public abstract class Component {
    public abstract void operation();
}
public class ConcreateComponent extends Component {
    @Override
    public void operation() {
        //需要被装饰的方法
        System.out.println("do something");
    }
}
public class Decorator extends Component {
    private Component component;
    public Decorator(Component component){
        this.component = component;
    }
    @Override
    public void operation() {
        //委托给装饰者执行
        component.operation();
    }
}
public class ConcreteDecorator extends Decorator {

    public ConcreteDecorator(Component component) {
        super(component);
    }

    private void method(){
        //do something
    }

    @Override
    public void operation() {
        this.method();
        super.operation();
    }
}

(这个装饰者模式和代理模式非常相近,而且都是用来增强被装饰类(被代理类)的;但是这两者的具体的区别和用法又是怎么样的呢)

  1. 装饰者模式应用

3.1装饰者模式的优点

  • 装饰类和被装饰类可以独立发展,而不会互相耦合。换句话说,Component类无须知道Decorator类,Decorator类是从外部来扩展Component类的功能,而Decorator也不用知道具体的构建。
  • 装饰者模式是继承模式的一个替代方案。我们看装饰类Decorator,不管装饰多少层,返回的对象还是Component,实现的还是is-a的关系。
  • 装饰模式可以动态地扩展一个实现类的功能,而且还不会影响到被扩展的类,非常符合开闭原则
    3.2装饰模式的缺点
    装饰者模式虽然可以很好的替代继承的方式处理问题,并且比继承更加灵活 ,但是也同样带来了和继承类似的问题,那就是装饰层数过多的问题,这个就只能灵活处理;
    3.3 装饰模式的使用场景
  • 需要扩展一个类的功能,或给一个类增加附加功能;
  • 需要动态地给一个对象增加功能,这些功能还可以动态的撤销;
  • 需要为一批的兄弟类进行改装或加装功能,当然是首选装饰模式
  1. 最佳实践

装饰模式是对继承的有力补充,而且装饰模式比继承更加的灵活,易维护,易扩展,衣服用;这个之前学习例子的时候就有很好的体会。
这里有一点就是装饰模式和代理模式的对比,这一章中并没有提出,有兴趣可以百度一下;我们学完23种设计模式后,会接着学后续的相似的设计模式之前的对比。

内容来自《设计模式之禅》

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容