Java设计模式之再从[暗黑破坏神"装备镶嵌宝石系统"]分析装饰(Decorator)模式

  在暗黑破坏神等RPG游戏中,会遇到如下的一些场景:我有一把很普通的武器,我通过给它“注入魔法力量”、“镶嵌宝石”,来使得它拥有一些攻击特效:例如,一把普通长剑,镶嵌红宝石后,可以附带火焰伤害,镶嵌了一个蓝宝石后,可以使得长剑攻击带有冰冻伤害。

  如何实现上述的机制呢?首先我们想到的是继承,因为,一个镶嵌了红宝石、蓝宝石的长剑,一定是一个镶嵌了宝石的长剑,并且一定一把长剑。如此下来,程序的结构会很复杂,而且你可能会想,要是镶嵌的是别的宝石怎么办呢?如果出了宝石外,可以镶嵌其他物品(如符文),那是不是应该把继承的结构改变呢?可见,继承并不是一个最优的方案。

  仔细分析一下这种情况,我们会发现,所谓“火焰伤害”、“冰冻伤害”,只是一些“攻击特效”,说白了就是长剑的攻击的装饰。我们仅仅是为了能够在长剑攻击的时候,增加一些职责:触发火焰伤害、触发冰冻伤害。这个时候我们就可以用到装饰(Decorator)模式——它的意图是动态地给一个对象添加一些额外的职责。就增加新功能来说,它比继承更为灵活。

  假设我想给我的长剑赋予眩晕、暴击和中毒攻击三种特效,那么Java代码可以这样写:

interface Weapon{
    void printInfo();
}

class Sword implements Weapon {
    public void printInfo(){ 
        System.out.println ("一把长剑");
    }
}

abstract class WeaponDecorator implements Weapon {
    public Weapon weapon;
    public WeaponDecorator(Weapon _weapon){
        weapon = _weapon;
    }
    public void printInfo(){
        weapon.printInfo();
    }
}

class PoisonDecorator extends WeaponDecorator {
    public PoisonDecorator(Weapon _weapon) {
        super(_weapon);
    }

    public void printInfo() {
        weapon.printInfo();
        //以下是自己添加的方法(装饰)
        System.out.println("  有50%几率造成敌人中毒");
    }
}

class CriticalDecorator extends WeaponDecorator {
    public CriticalDecorator(Weapon _weapon) {
        super(_weapon);
    }

    public void printInfo() {
        weapon.printInfo();
        //以下是自己添加的方法(装饰)
        System.out.println("  有50%几率造成致命一击");
    }
}

class DazzleDecorator extends WeaponDecorator {
    public DazzleDecorator(Weapon _weapon) {
        super(_weapon);
    }

    public void printInfo() {
        weapon.printInfo();
        //以下是自己添加的方法(装饰)
        System.out.println("  有50%几率造成敌人眩晕");
    }
}

class Decorator
{
    public static void main(String[] args) {
        Weapon superWeapon = new DazzleDecorator(new CriticalDecorator(new PoisonDecorator(new Sword())));
        superWeapon.printInfo();
    }
}

  武器都继承于Weapon接口,此接口声明了一个方法:printInfo(),将武器的信息打印出来。我们的装饰器类WeaponDecorator中,包含了一个原始的Weapon对象引用,接下来是重点:当调用装饰器类的printInfo时,它会先调用Weapon对象的printInfo,然后再调用自己额外增加的一些代码。当然,调用Weapon.printInfo的顺序可先可后,但是一定会调用到。也就是说,装饰器类的重点是其中包含一个原始对象,在调用某个方法时,装饰器类不仅仅会调用原始对象的这个方法,你还可以自己给它增加一些方法。如本例中,调用装饰器类的printInfo时,先调用了weapon.printInfo(),再调用了我们希望它增加的职责System.out.println,那么代码运行结果如下:

一把长剑
  有50%几率造成敌人中毒
  有50%几率造成致命一击
  有50%几率造成敌人眩晕

  装饰模式比继承模式更加灵活(可以灵活添加职责),然而,我们必须要为每一个被装饰的方法新建一个类,如果需要装饰的方法很多,那么我们会需要建立数量巨大的装饰器类,这个时候就要考虑是否要用装饰模式了。在Java.IO,C#的IO(如OutputStream)中就用到了装饰器这种模式。需要注意到是,装饰器类必须要与被装饰的类继承于同一接口,这就如最开始所说的,不管长剑镶嵌了什么,它总是一把武器。

 

 


你可能感兴趣的:(java,设计模式,Decorator,装饰模式)