您的位置:首页 > 移动开发 > Android开发

【设计模式无难事】——装饰者

2018-01-11 16:04 183 查看
【设计模式无难事】——装饰者



一、意图

装饰者模式,顾名思义,通过给一个对象提供装饰作用,从而使对象得到增强。

这里可能有人会问,继承也可以达到同样目的,为什么需要装饰者?没错,继承的确也可以,但是它有明显的不足,我们现在就通过一个实际例子来说明

二、例子

需求场景

游戏中的怪物(簇)实现

包括:基础怪物、水系怪物、火系怪物,大魔王

分析&实作

(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("来了一
4000
个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

谢谢
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息