【设计模式无难事】——装饰者
一、意图
装饰者模式,顾名思义,通过给一个对象提供装饰作用,从而使对象得到增强。
这里可能有人会问,继承也可以达到同样目的,为什么需要装饰者?没错,继承的确也可以,但是它有明显的不足,我们现在就通过一个实际例子来说明
二、例子
需求场景
游戏中的怪物(簇)实现
包括:基础怪物、水系怪物、火系怪物,大魔王
分析&实作
(1)装饰者方式实现
1,假定所有的怪物都有2个共同的行为:攻击,逃跑。于是我们定义一个接口
Troll.java
/**
*
* Interface for trolls
* 精灵
*/
public interface Troll {
void attack();
void fleeBattle(); //临阵脱逃
}
2,接着我们做一个简单的怪物
SimpleTroll.java
/**
*
* SimpleTroll implements {@link Troll} interface directly.
*
*/
public class SimpleTroll implements Troll {
@Override
public void attack() {
LOGGER.info("怪物跑向你,试图抓住你!");
}
@Override
public void fleeBattle() {
LOGGER.info("怪物逃跑了!");
}
}
3,接下来,水系怪物
WaterTroll.java
/**
* Decorator that adds a icicle for the troll
*/
public class WaterTroll implements Troll {
private static final Logger LOGGER = LoggerFactory.getLogger(WaterTroll.class);
private Troll decorated;/*被装饰的怪物*/
public WaterTroll(Troll decorated) {
this.decorated = decorated;
}
@Override
public void attack() {
decorated.attack();
/*不但有被装饰怪物的攻击,还额外增加了冰霜攻击*/
LOGGER.info("怪物用冰柱攻击了你");
}
@Override
public void fleeBattle() {
decorated.fleeBattle();
}
}
4,火系怪物
FireTroll.java
/**
* Decorator that adds a fireBall for the troll
*/
public class FireTroll implements Troll {
private static final Logger LOGGER = LoggerFactory.getLogger(FireTroll.class);
private Troll decorated;
public FireTroll(Troll decorated) {
this.decorated = decorated;
}
@Override
public void attack() {
decorated.attack();
/*不但有被装饰怪物的攻击,还额外增加了火球攻击*/
LOGGER.info("怪物用火球攻击了你!");
}
@Override
public void fleeBattle() {
decorated.fleeBattle();
}
}
5,Boss
BossTroll.java
/**
* Decorator that adds a boss attack for the troll
*/
public class BossTroll implements Troll {
private static final Logger LOGGER = LoggerFactory.getLogger(BossTroll.class);
private Troll decorated;
public BossTroll(Troll decorated) {
this.decorated = decorated;
}
@Override
public void attack() {
decorated.attack();
/*不但有被装饰怪物的攻击,还额外增加了boss攻击*/
LOGGER.info("boss攻击");
}
@Override
public void fleeBattle() {
decorated.fleeBattle();
}
}
6,运行
public static void main(String[] args) {
// simple troll
LOGGER.info("来了一个基础怪物");
Troll simpleTroll = new SimpleTroll();
simpleTroll.attack();
simpleTroll.fleeBattle();
// change the behavior of the simple troll by adding a decorator
LOGGER.info("来了一个水系怪物");
Troll waterTroll = new WaterTroll(simpleTroll);
waterTroll.attack();
waterTroll.fleeBattle();
LOGGER.info("来了一个火系怪物");
Troll fireTroll = new FireTroll(simpleTroll);
fireTroll.attack();
fireTroll.fleeBattle();
LOGGER.info("来了一个Boss");
Troll bossTroll = new BossTroll(fireTroll/*waterTroll|simpleTroll*/);
bossTroll.attack();
bossTroll.fleeBattle();
}
}
7,输出
15:41:23.755 [main] INFO com.iluwatar.decorator.App - 来了一个基础怪物
15:41:23.757 [main] INFO com.iluwatar.decorator.SimpleTroll - 怪物跑向你,试图抓住你!
15:41:23.758 [main] INFO com.iluwatar.decorator.SimpleTroll - 怪物逃跑了!
15:41:23.758 [main] INFO com.iluwatar.decorator.App - 来了一个水系怪物
15:41:23.758 [main] INFO com.iluwatar.decorator.SimpleTroll - 怪物跑向你,试图抓住你!
15:41:23.758 [main] INFO com.iluwatar.decorator.WaterTroll - 怪物用冰柱攻击了你
15:41:23.758 [main] INFO com.iluwatar.decorator.SimpleTroll - 怪物逃跑了!
15:41:23.758 [main] INFO com.iluwatar.decorator.App - 来了一个火系怪物
15:41:23.758 [main] INFO com.iluwatar.decorator.SimpleTroll - 怪物跑向你,试图抓住你!
15:41:23.758 [main] INFO com.iluwatar.decorator.FireTroll - 怪物用火球攻击了你!
15:41:23.758 [main] INFO com.iluwatar.decorator.SimpleTroll - 怪物逃跑了!
15:41:23.758 [main] INFO com.iluwatar.decorator.App - 来了一个Boss
15:41:23.759 [main] INFO com.iluwatar.decorator.SimpleTroll - 怪物跑向你,试图抓住你!
15:41:23.759 [main] INFO com.iluwatar.decorator.FireTroll - 怪物用火球攻击了你!
15:41:23.759 [main] INFO com.iluwatar.decorator.BossTroll - boss攻击
15:41:23.759 [main] INFO com.iluwatar.decorator.SimpleTroll - 怪物逃跑了!
Process finished with exit code 0
(2)继承方式实现
WaterTroll.java 和 BossTroll.java
public class WaterTroll extends SImpleTroll {
private static final Logger LOGGER = LoggerFactory.getLogger(WaterTroll.class);
@Override
public void attack() {
super.attack();
/*不但有父类的攻击,还额外增加了冰霜攻击*/
LOGGER.info("怪物用冰柱攻击了你");
}
@Override
public void fleeBattle() {
super.fleeBattle();
}
}
public class BossTroll extends fireTroll{
private static final Logger LOGGER = LoggerFactory.getLogger(BossTroll.class);
@Override
public void attack() {
super.attack();
/*不但有父类的攻击,还额外增加了boss攻击*/
LOGGER.info("boss攻击");
}
@Override
public void fleeBattle() {
super.fleeBattle();
}
}
三、思考
看完上面的例子,我们可以看到二者的区别了
1,继承是静态的,而装饰者是动态的;
用继承,BossTroll在编译阶段就固定为fireTroll的子类;
用装饰者,BossTroll在运行时才决定是要增强fireTroll对象或是waterTroll对象,这可以由服务器端动态下发,运行时来决定是火系boss还是水系boss,或是一个simpleTroll的boss。
2,装饰者没有继承体系约束,更加灵活
随着怪物种类的剧增,如果使用继承,难免会导致继承树十分庞大,而且,精准的拿捏继承体系的合理抽象也是困难的,任何层级的多余行为都会污染继承体系,不是吗?
而装饰者却可以拿出原本属于继承树任意层级中的对象来装饰,而得到新的增强对象,这点也是继承机制难以实现的。
3,还有一个非常适合装饰者的例子“星巴克咖啡”,这里就不贴代码了,如果想把
“咖啡”、“加奶咖啡”、“加糖咖啡”、“加糖加奶咖啡”、“加伴侣咖啡”、“摩卡咖啡”、“黑咖啡”、“加糖加奶摩卡咖啡”。。。。等概念组织起来的话,非用装饰者不可,继承会要了你的命。
Tips:一个类,它实现了某个接口,并持此接口的引用,是装饰者的特征
四、装饰者模式在android源码中的例子
Context、ContextImpl、ContextThemeWrapper、Activity
关于这个网上已经有大量的帖子了
附:
本文源代码在这里
有任何问题,欢迎留言或Email我 zzy_2002@126.com
转载请注明出处:http://blog.csdn.net/zzy_801011/article/details/79035321
谢谢