在暗黑破坏神等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)模式,布布扣,bubuko.com