手记

戏说设计模式 - 给你一个巴掌,再给你一颗蜜枣,能干否? - 装饰模式

设计模式 - 装饰模式

[toc]

简介

Decorator

- 作用

  • 装饰者是一种实现继承的替代方案。 当脚本运行时,在子类中增加行为会影响原有类所有的实例,而装饰者却不然,取而代之的是它能给不同对象各自添加新行为
  • 添加辅助的额外功能
  • 把类的核心职责和装饰功能区分开了

角色
  • Component
    抽象构件角色:定义一个对象接口,可以给这些对象动态地添加职责

  • ConcreteComponent
    具体构件角色:定义一个对象,可以给这个对象添加一些职责

  • Decorator
    装饰角色:维持一个指向Component对象的指针,即必有一个Component的private变量,并定义一个与Component接口一致的接口

  • ConcreteDecorator
    具体装饰角色:向组件添加职责
优缺点

- 优点

  • 比继承更灵活 :一个特定的Component类提供多个不同的Decorator类,可以使得我们对一些职责进行混合和匹配。
  • 避免在层次结构高层的类有太多的特征:Decorator模式提供了一种 “ 即用即付 ” 的方法来添加职责。它并不试图在一个复杂的可定制的类中支持所有可预见的特征,相反,你可 以定义一个简单的类,并且用Decorator类给它 逐渐地添加功能。可以用简单的部件组合出复杂的功能,使应用程序不比为不需要的特征付出代价。
  • 降低类的复杂度:将类中的装饰功能从类中搬移出去,有效的把类中的核心职责和装饰功能区分开了,可以去除相关类中重复的装饰逻辑。

- 缺点

  • 装饰模式会导致设计中出现许多小类,如果过度使用,会使程序变得很复杂。
  • 多层装饰比较复杂
适用场景
  • 对象由主体+许多可选的部件/功能构成
注意事项
  • 装饰的类和被装饰的类,要求拥有相同的访问接口方法
  • 装饰的类要有对被装饰类的引用,用于在装饰类的相应方法,调用相应被装饰类的方法,然后对其进行修饰
  • 把每个要装饰的功能放在单独的方法里面
案例

给你一个巴掌,再给你一颗蜜枣,能干否?

大牛:“小鸟,吃饭了。”

小鸟:“哦!来了。”

大牛:“咋啦,咋还闷闷不乐呢?”

小鸟:“今天我们开了个讨论会,然后我针对我们组长提出的想法,说了几点不足,并谈了一下我自己的想法。结果我组长一天都没搭理我。”

大牛:“你是不是在指出你组长想法不足的时候,特别激动和happy。”

小鸟:“happy 到没有,小激动还是有的,毕竟别人都没发现不足,就我发现了,还提出了更好的建议,给大boss留了个能干的好印象啊!”

大牛:“小鸟啊!你这样很容易英年早逝哦!”

大牛:“你说,我啪啪啪的打你一巴掌,再给你个蜜枣。”

大牛:“你是这样呢?”

大牛:“还是这样呢?”

大牛:“亦或是这样”

小鸟:“我肯定是这样的。”

大牛:“对咯!所以说咱们说话得含蓄一点。”

小鸟:“如果不能含蓄呢?”

大牛:“实时求是,别这么得瑟啊!”

大牛:“来,赶紧吃饭吧,吃完饭我还有任务给你。”

小鸟:“啥任务啊?”

大牛:“就是让你以你这次说话为题,简单写个控制台程序呗?”

小鸟:“牛哥,你不是拿我开刷吧?以说话为题,这还不容易吗?就只写定义一个说话的类,然后里面带上一个说话的方法呗?”

小鸟:“牛哥,你别光笑啊,笑的我背后直冒冷汗,你帮我看看,是这样吗?”
说话类

public class Say {

        public void talk() {
            System.out.print("我指出了组长方案不对的地方");
        }

        public void react(String string) {
            System.out.print("\n\n组长听后的反应:"+string);
        }
    }

测试

Say say = new Say();
    say.talk();
    say.react("组长很生气,后果很严重,啥也不跟我说,呜呜...");

结果


大牛:“还记得我们以前说的依赖倒置原则吗?”

小鸟:“要面向接口编程,不要面向实现编程,我马上改.”

说话的抽象类

public abstract class Say {
        // 我说话
        public abstract void talk();
        // 希望得到组长表扬
        public abstract void react(String string);
    }

说话的具体实现类

public class XiaoNiaoSay extends Say {

        @Override
        public void talk() {
            System.out.print("我指出了组长方案不对的地方");
        }

        @Override
        public void react(String string) {
            System.out.print("\n\n组长听后的反应:"+string);
        }
    }

测试

Say say = new XiaoNiaoSay();
    say.talk();
    say.react("组长很生气,后果很严重,啥也不跟我说,呜呜...");

结果


大牛:“是呢!组长很生气,后果很严重。”

大牛:“你在提自己的想法之前,如果能先肯定一下你们组长方案的优点的话,说不定你们组长不会这么生气哦!”

小鸟:“也是哈。”

大牛:“别哈了,麻溜的,把程序改一下呗。”

小鸟:“这还不容易吗?在“XiaoNiaoSay”中加一个赞美组长方案的方法呗!哎呀,不对,这好像违反了开-闭原则。”

大牛:“对头。别学这,丢那哦!”

小鸟:“代码出来了,我用的继承。”
新学的说话的技巧:PraiseXiaoNiaoSay

public class PraiseXiaoNiaoSay extends XiaoNiaoSay {

        private void praiseManager() {
            System.out.println("组长这个方案的优点有:...\n");
        }

        /**开口说话前,为了照顾我们组长幼小的心灵,先赞美下我们组长的方案*/
        @Override
        public void talk() {
            praiseManager();
            super.talk();
        }
    }

测试

Say say = new PraiseXiaoNiaoSay();
    say.talk();
    say.react("老啦老啦,比不了年轻人咯!");

结果

小鸟:“这次组长虽然没表扬我,但也不至于不搭理我了。”


装饰模式实现

大牛:“确实能解决这个问题,我们现在来假设一下,你们组长的大男子主义特别严重,听不得别人对他有任何反对意见,可想而知,你这次就算是彻底把他给得罪了,你又不就准备撤,如果你不想撤,你在前面肯定了他方案优点这还不够,说不得,得再想办法贿赂贿赂人家。要是人家就不肯原谅你,那你不得学刘备三顾茅庐啥的,说不定更多顾呢?咱们这里不说太多,就10顾吧,这要按继承来弄?”

小鸟:“这样的话,子类会多的不要不要的,受不了了,不行,牛哥,你是不是有啥好办法啊?”

大牛:“这是必须的啦!”

大牛:“来,先给你看个类图。”

Component --> Say
小鸟:“抽象类Say不变,就是看成我们这个类图中的Component类就行了”

public abstract class Say {
        // 我说话
        public abstract void talk();
        // 希望得到组长表扬
        public abstract void react(String string);
    }

ConcreteComponent --> XiaoNiaoSay
小鸟:“我说的话XiaoNiaoSay类也不变,看成类图中的ConcreteComponent类就可以了。”

public class XiaoNiaoSay extends Say {

        @Override
        public void talk() {
            System.out.print("我指出了组长方案不对的地方");
        }

        @Override
        public void react(String string) {
            System.out.print("\n\n组长听后的反应:"+string);
        }

    }

Decorator --> SayDecorator
小鸟:“SayDecorator是根据类图添加的,相当于类图中的Decorator”

public abstract class SayDecorator extends Say {
        private Say mSay;

        public SayDecorator(Say say) {
            mSay = say;
        }

        @Override
        public void talk() {
            if (mSay != null) {
                mSay.talk();
            }
        }

        @Override
        public void react(String string) {
            if (mSay != null) {
                mSay.react(string);
            }
        }
    }

ConcreteDecorator --> PraiseSayDecorator
小鸟:“PraiseSayDecorator是由以前的PraiseXiaoNiaoSay改编而来,因为你说过继承会导致子类的层次增加,而且耦合度太高,不灵活,所以我这改了一下,将它继承的类改成了SayDecorator,其他没变,只添加了一个构造方法。”

public class PraiseSayDecorator extends SayDecorator {

        public PraiseSayDecorator(Say say) {
            super(say);
        }

        private void praiseManager() {
            System.out.println("组长这个方案的优点有:...\n");
        }

        /**开口说话前,为了照顾我们组长幼小的心灵,先赞美下我们组长的方案*/
        @Override
        public void talk() {
            praiseManager();
            super.talk();
        }
    }

测试
小鸟:“这是客户端调用。”

Say xiaoNiaoSay = new XiaoNiaoSay();
    SayDecorator praiseSayDecorator = new PraiseSayDecorator(xiaoNiaoSay);
    praiseSayDecorator.talk();
    praiseSayDecorator.react("老了老了,比不了年轻人了");

结果
小鸟:“这是结果,组长虽没表扬我,但也不至于不搭理我啊!”

大牛:“不错,改编的不错,咱们还是那个假设,如果你组长想摆点谱,让你三顾茅庐咋办?”

小鸟:“这还不简单吗!增加3个SayDecorator的子类.”

  • 一顾茅庐:AlcoholSayDecorator
    小鸟:“我带上酒,去看我们组长”
public class AlcoholSayDecorator extends SayDecorator {
        public AlcoholSayDecorator(Say say) {
            super(say);
        }

        private void alcohol() {
            System.out.print("\n\n组长,咱们来喝俩杯吧?");
        }

        @Override
        public void react(String string) {
            alcohol();
            super.react(string);
        }
    }

小鸟:“这是客户端调用”

Say xiaoNiaoSay = new XiaoNiaoSay();
    SayDecorator praiseSayDecorator = new PraiseSayDecorator(xiaoNiaoSay);
    // 一顾茅庐:带上酒去看我们组长
    SayDecorator alcoholSayDecorator = new AlcoholSayDecorator(praiseSayDecorator);
    alcoholSayDecorator.talk();
    alcoholSayDecorator.react("<心里活动>以为喝点酒就想让我原谅你?想让我说话没门"); 

小鸟:“老头居然嫌弃,带茅台都不搭理我”

  • 二顾茅庐:ChessSayDecorator
    小鸟:“老头都喜欢下棋,我去找他下棋,准行。”
public class ChessSayDecorator extends SayDecorator {

        public ChessSayDecorator(Say say) {
            super(say);
        }

        private void chess() {
            System.out.print("\n\n 组长,咱来杀两盘?");
        }

        @Override
        public void react(String string) {
            chess();
            super.react(string);
        }
    }

小鸟:“然后客户端调用改成这样.”

Say xiaoNiaoSay = new XiaoNiaoSay();
    SayDecorator praiseSayDecorator = new PraiseSayDecorator(xiaoNiaoSay);

    // 一顾茅庐:带上酒去看我们组长
    SayDecorator alcoholSayDecorator = new AlcoholSayDecorator(praiseSayDecorator);
    // 二顾茅庐:找组长下棋
    SayDecorator chessSayDecorator = new ChessSayDecorator(alcoholSayDecorator);

    chessSayDecorator.talk();
    chessSayDecorator.react("<心里活动>也太把我当普通人了,以为我跟其他老头一样喜欢下棋?想让我表扬你,想都不用想");

  • 三顾茅庐:
    小鸟:“看来老头不能用常理来认知,去找他打羽毛球,看行不行。”
public class BadmintonSayDecorator extends SayDecorator {

        public BadmintonSayDecorator(Say say) {
            super(say);
        }

        private void badminton() {
            System.out.print("\n\n组长,听说你家旁边新开了一家羽毛球馆,咱们去打一局啊!");
        }

        @Override
        public void react(String string) {
            badminton();
            super.react(string);
        }
    }

小鸟:“永恒不动的客户端的调用.”

Say xiaoNiaoSay = new XiaoNiaoSay();
    SayDecorator praiseSayDecorator = new PraiseSayDecorator(xiaoNiaoSay);

    // 一顾茅庐:带上酒去看我们组长
    SayDecorator alcoholSayDecorator = new AlcoholSayDecorator(praiseSayDecorator);
    // 二顾茅庐:找组长下棋
    SayDecorator chessSayDecorator = new ChessSayDecorator(alcoholSayDecorator);
    // 三顾茅庐:找组长打羽毛球球
    SayDecorator badmintonSayDecorator = new BadmintonSayDecorator(chessSayDecorator);

    badmintonSayDecorator.talk();
    badmintonSayDecorator.react("年轻人,不错,世界该是你们的,我们老了,该退休咯");

小鸟:“完美的结局”

大牛:“真的吗?你看看这啊!”

小鸟:“哎呀,这顺序不对啊,如果真这样的话,好不容易搞定,又得再折腾一次了。伤不起啊!真的伤不起,我和你和你打的混天黑地...”
大牛:“行了行了,别贫了,能改不?”
小鸟:“容易啊!这不就是个顺序问题吗,我把三顾茅庐的调用顺序改一下就ok了。”

Say xiaoNiaoSay = new XiaoNiaoSay();
    SayDecorator praiseSayDecorator = new PraiseSayDecorator(xiaoNiaoSay);
    // 三顾茅庐:找组长打羽毛球球
    SayDecorator badmintonSayDecorator = new BadmintonSayDecorator(xiaoNiaoSay);
    // 二顾茅庐:找组长下棋
    SayDecorator chessSayDecorator = new ChessSayDecorator(badmintonSayDecorator);
    // 一顾茅庐:带上酒去看我们组长
    SayDecorator alcoholSayDecorator = new AlcoholSayDecorator(chessSayDecorator);

    alcoholSayDecorator.talk();
    alcoholSayDecorator.react("年轻人,不错,世界该是你们的,我们老了,该退休咯");

小鸟:“这样就ok,但是我感觉这个有点坑啊!这个顺序一不留神就可就把我给坑死了。”
大牛:“这就是装饰模式的优点所在,同时也是缺点。非常的灵活,可以任意的去组合,但同时由于对顺序没严格控制,导致设计容易,但后期学习与维护成本比较高,排错难。
小鸟:“这样子啊?”
大牛:“这什么表情啊!你使用这个模式的时候,只要不用在对顺序有严格要求的地方就行了,毕竟不同的装饰顺序,会造成不一样的效果,会让你感觉世界更精彩哦,同时它将核心部分与装饰部分进行了分离,降低了系统复杂度.

优化

小鸟:“如果Component的子类有且只有一个呢?我还要这样写吗?能将Component与它的子类ConcreteComponent合并不,直接让Decorator继承ConcreteComponent?”

大牛:“这个当然可以了咯!如果你的Decorator的子类ConcreteDecorator也是有且只有一个的话,也可以将它俩合并,直接用ConcreteDecorator去继承Component哦!”

小鸟:“其实我们组长人还是不错的,平时有问题问他,他都会耐心的教我呢?就是可能觉得我这次让他在boss面前丢了面子,生气了。看来我的去好好哄哄他,怎么哄捏?”

大牛:“好了好了,看你说,你们组长人还是不错的,想必过两天就好了。赶紧吃饭吧!”

4人推荐
随时随地看视频
慕课网APP