装饰者模式(包装一个对象,来提供新的行为)

源码地址 https://github.com/DingMouRen/DesignPattern
装饰者模式.png
  • Component:抽象组件,可以是一个接口或者抽象类,是被装饰的原始对象。
  • ConcreteComponent:组件具体实现类。该类是Component类的基本实现,也是我们装饰的具体对象。
  • Decorator:抽象装饰者。它承担的职责是为了装饰组件对象,其内部一定要有一个指向组件对象的引用。在多数情况下,该类为抽象类,需要根据不同的装饰逻辑实现不同的具体子类。如果装饰逻辑单一,只有一个情况下我们可以省略该类直接作为具体的装饰者。
  • ConcreteDecoratorA:装饰者具体实现类,只是对抽象装饰者作出具体的实现。
定义

装饰者模式动态地给一个对象添加一些额外的职责。在增加功能方面,装饰者模式比生成子类更为灵活。

使用场景
  • 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加功能
  • 处理可以撤销的职责
  • 当不能采用生成子类的方式进行扩展功能时。1.为类扩展功能造成产生大量子类,子类数目爆炸性增长。2.不能生成子类的情况,比如被final修饰的类
协作

Decorator将请求转发给它的Component对象,并有可能在转发请求前后执行一些附加的动作,也就是想要添加的新行为。

举个栗子

不管男的女的都是要穿衣服的,我们抽象成一个抽象类Person,行为就是穿衣服,定义一个穿衣服的抽象方法dressed(),定义两个类Boy、Girl分别继承Person类,它们只有一个行为。假设Boy Girl都通过调用自己实现的dressed()方法,已经穿了一件衣服,可是人不能只穿一件衣服吧(o)/~,但是我们不想去修改Boy Gril的对象,同时让创建出来的这两个实例对象能穿更多的衣服,也就是说在不影响对象的情况下,为对象添加功能。好啦,可以开始使用装饰者模式了。我们定义一个装饰的抽象类PersonCloth,让它继承抽象组件Person类,同时我们让PersonCloth类持有一个Person类的引用,通过构造器传入。因为继承了Person类,就要实现dressed()这个抽象方法,里面的具体实现自然是Person类的引用调用dressed(),这就是保存这个引用的主要原因,可以方便的调用具体被装饰对象的dressed()方法(java运行时类型判断)。现在我们要定义装饰者的具体实现对象,定义CheapCloth类继承PersonCloth类,实现dressed()方法,这里面有一个super.dressed()这就是被装饰对象自己原来的实现,我们想添加的行为怎么办呢?只要在CheapCloth这个具体装饰对象中定义新的行为,然后在super.dressed()前或者后调用就可以了,这样就添加了功能,调用的时候自然调用的是这个装饰者对象CheapCloth的dressed()方法。就好像通过CheapCloth类包裹了Boy类一样,我们没有动Boy这样的具体组件对象,也没有使用继承可能会造成类爆炸的方式。

//抽象组件类:类Person定义一个穿衣的抽象方法
public abstract class Person {
    public abstract void dressed();
}
//组件具体实现类:需要被装饰的具体对象
public class Boy extends Person {
    @Override
    public void dressed() {
        System.out.println(getClass().getSimpleName()+"穿牛仔褂");
    }
}

//装饰抽象类:表示人要穿的衣服
public abstract class PersonCloth  extends Person{
    /**
     * 保持一个Person类的引用,方便调用具体被装饰对象中的方法
     * 这样可以在不破坏原类层次结构的情况下为类添加一些功能,只需要在被装饰对象的相应方法
     * 前或后增加相应的逻辑功能就行。
     * 如果装饰物只有一个的话,不必声明一个抽象类作为装饰者抽象的提取。只要定义一个普通的类表示装饰者就行
     */
    private Person person;

    public PersonCloth(Person person) {
        this.person = person;
    }

    @Override
    public void dressed() {
        person.dressed();//调用Person类型的dressed()方法
    }

    public Person getPerson() {
        return person;
    }
}

//具体装饰者
public class CheapCloth extends PersonCloth {

    public CheapCloth(Person person) {
        super(person);
    }

    @Override
    public void dressed() {
        //原来具体组件实现
        super.dressed();
        //添加的新行为的具体实现
        dressShorts();
    }

    private void dressShorts(){
        System.out.println(getPerson().getClass().getSimpleName()+"穿短裤");
    }
}

public static void main(String[] args) {
        //创建被装饰对象
        Person person = new Boy();
        //给他穿便宜衣服
        PersonCloth clothCheap = new CheapCloth(person);
        clothCheap.dressed();
        //穿贵的衣服
        PersonCloth clothExpensive = new ExpensiveCloth(person);
        clothExpensive.dressed();


        Person girl = new Girl();
        PersonCloth clothCheapGirl = new CheapCloth(girl);
        clothCheapGirl.dressed();
    }

使用

public static void main(String[] args) {
        //创建被装饰对象
        Person person = new Boy();
        //给他穿便宜衣服
        PersonCloth clothCheap = new CheapCloth(person);
        clothCheap.dressed();
        //穿贵的衣服
        PersonCloth clothExpensive = new ExpensiveCloth(person);
        clothExpensive.dressed();


        Person girl = new Girl();
        PersonCloth clothCheapGirl = new CheapCloth(girl);
        clothCheapGirl.dressed();
    }
总结

装饰者模式为所装饰的对象增加功能,而不使用继承的方式,也不会影响被装饰对象。有的时候会跟代理模式混淆,代理模式做的不是增加功能,而是对代理的对象进行控制。

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

推荐阅读更多精彩内容

  • 1 场景问题# 1.1 复杂的奖金计算## 考虑这样一个实际应用:就是如何实现灵活的奖金计算。 奖金计算是相对复杂...
    七寸知架构阅读 3,954评论 4 67
  • 设计模式汇总 一、基础知识 1. 设计模式概述 定义:设计模式(Design Pattern)是一套被反复使用、多...
    MinoyJet阅读 3,903评论 1 15
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,581评论 18 399
  • 放慢脚步,在校园 把呼吸折叠于心, 我看见 每一朵花都在奔跑 花影乱 双手捧出 风——舞动多彩的眼睛 48颗柔软...
    雪儿金阅读 151评论 0 0
  • 苏寒此话一出,大厅当中立刻寂静了下来。 显然谁都没有想到,以苏寒此刻的情况,还敢这般对苏云琛说话。 只有坐在主...
    魔笔判官阅读 392评论 0 1