设计模式 - 装饰模式
[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() {
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面前丢了面子,生气了。看来我的去好好哄哄他,怎么哄捏?”
大牛:“好了好了,看你说,你们组长人还是不错的,想必过两天就好了。赶紧吃饭吧!”