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

【设计模式无难事】——装饰者
这里写图片描述
一、意图
装饰者模式,顾名思义,通过给一个对象提供装饰作用,从而使对象得到增强。
这里可能有人会问,继承也可以达到同样目的,为什么需要装饰者?没错,继承的确也可以,但是它有明显的不足,我们现在就通过一个实际例子来说明

二、例子
需求场景
游戏中的怪物(簇)实现
包括:基础怪物、水系怪物、火系怪物,大魔王

分析&实作
(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
谢谢

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值