设计模式之装饰者模式

一、简介

一种结构型模式,它可以在不改变原有对象的情况下,动态的给一个对象扩展新的功能。

一般包括:

  • 抽象的被装饰者类(里面定义一些将要被扩展的功能)、
  • 被装饰者类(实现基础功能)、
  • 抽象的装饰者类(继承抽象的被装饰者类,一般包含一个将要被扩展的被装饰者和声明一些装饰者的特定功能)
  • 装饰者类(传入一个被装饰者,重写方法,实现功能扩展)

需要注意的是装饰者模式也建立在继承的基础之上的,类似于继承(装饰者和被装饰者继承同一个类,以便装饰者扩展被装饰者的功能以及装饰者作为被装饰者)+聚合复用(复用被装饰者的功能)

即扩展的功能AB=装饰者中重写功能A(){被装饰者的功能A+额外的功能B},其中原有功能A相当于被装饰者,额外的功能B相当于装饰者,由于装饰者和被装饰者继承了同一个类,扩展的功能AB又可以作为被装饰者装饰,如此一直嵌套下去,其行为类似于递归,返回的条件就是基础功能A。

二、优缺点

它的优点是:

  • 它比继承更加灵活。
    因为在有些情况下,需要扩展的功能的种类很多,比如对于基础功能A,我们想要得到AB,AC,ABB等扩展的功能,势必生成很多子类,增加系统的复杂性,同时,使用继承实现功能扩展,我们必须事先知道所有要实现的扩展功能,然后以每个扩展功能分别对应一个子类的形式来实现它们,也就是这些功能是编译时就确定了,是静态的。
    而使用装饰者模式可以在运行时决定,通过使用不同装饰类以及这些类的排列组合,可以动态的实现不同的扩展功能。

  • 另外在扩展功能时也符合开闭原则
    即如果想扩展被装饰者类的行为,无须修改装饰者抽象类,只需继承装饰者抽象类,实现额外的一些装饰或者叫行为即可对被装饰者进行包装

它的缺点是动态装饰时,多层装饰时会更复杂(使用继承来拓展功能会增加类的数量,使用装饰者模式不会像继承那样增加那么多类的数量但是会增加对象的数量,当对象的数量增加到一定的级别时,无疑会大大增加我们代码调试的难度)

三、举例

1.定义抽象的被装饰者(面条)

abstract class Noodle{
    public abstract String getDesc();
    public abstract int getPrice();
}

2.定义具体的被装饰者(炒面),被装饰者的初始状态或者称为主体

class FriedNoodle extends Noodle{
    @Override
    public String getDesc() {
        return "炒面 ";
    }
    @Override
    public int getPrice() {
        return 10;
    }
}

3.定义抽象的装饰者(和面相关的一些调料)

abstract  class Others extends Noodle{
    private Noodle noodle;//将要被装饰的被装饰者
    public Others(Noodle noodle){
        this.noodle=noodle;
    }
    public Noodle getNoodle() {
        return noodle;
    }
    public abstract void printInfo();
}

4.定义具体的装饰者(如鸡蛋,肉丝)——鸡蛋

class NoodleWithEgg extends Others{
    public NoodleWithEgg(Noodle noodle){
        super(noodle);//初始化被装饰者
    }
    @Override
    public void printInfo() {
        if(getNoodle() instanceof  Others){
            ((Others)getNoodle()).printInfo();
        }
        System.out.println("装饰者为:鸡蛋,"+"被装饰者为:"+getNoodle().getDesc());
    }
    @Override
    public String getDesc() {
        return getNoodle().getDesc()+"加一个鸡蛋 ";
    }

    @Override
    public int getPrice() {
        return getNoodle().getPrice()+1;
    }   
}

4.定义具体的装饰者(如鸡蛋,肉丝)——肉丝

class NoodleWithMeat extends Others{
    public NoodleWithMeat(Noodle noodle){
        super(noodle);//初始化被装饰者
    }
    @Override
    public void printInfo() {
        if(getNoodle() instanceof  Others){
            ((Others)getNoodle()).printInfo();
        }
        System.out.println("装饰者为:肉丝,"+"被装饰者为:"+getNoodle().getDesc());
    }
    @Override
    public String getDesc() {
        return getNoodle().getDesc()+"加一份肉丝 ";
    }

    @Override
    public int getPrice() {
        return getNoodle().getPrice()+2;
    }   
}

测试:

public class 装饰者模式 {

    public static void main(String[] args) {
        //订一份炒粉,加两个鸡蛋,一份肉丝(最外层的装饰者是肉丝,被装饰者是炒粉+两个鸡蛋)
        //1.先做好主体——炒粉
        FriedNoodle friedNoodle=new FriedNoodle();
        //2.用一个鸡蛋对炒粉进行装饰
        NoodleWithEgg egg1_Noodle=new NoodleWithEgg(friedNoodle);
        //3.再用一个鸡蛋对上面的(1个鸡蛋+炒粉)进行装饰
        NoodleWithEgg egg2_Noodle=new NoodleWithEgg(egg1_Noodle);
        //4.再用一份肉丝对上面的(2个鸡蛋+炒粉)进行装饰,大功告成
        NoodleWithMeat myNoodle=new NoodleWithMeat(egg2_Noodle);
        //显示我们的订餐信息,及价格
        System.out.println(myNoodle.getDesc()+"价钱:"+myNoodle.getPrice());
        //显示我们的装饰过程
        myNoodle.printInfo();
    }

}

运行结果:

炒面 加一个鸡蛋 加一个鸡蛋 加一份肉丝 价钱:14
装饰者为:鸡蛋,被装饰者为:炒面 
装饰者为:鸡蛋,被装饰者为:炒面 加一个鸡蛋 
装饰者为:肉丝,被装饰者为:炒面 加一个鸡蛋 加一个鸡蛋 

四、扩展

装饰者和代理模式
装饰者模式关注的是对象的动态添加功能。代理模式关注的是对对象的控制访问,对它的用户隐藏对象的具体信息。

五、参考

设计模式(三)—— 装饰者模式
JAVA设计模式初探之装饰者模式
装饰者模式

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