奇幻RPG(物品锻造 与 Decorator模式)

引言

物品锻造是各类奇幻游戏中的常见功能,就拿众所周知的Diablo来说吧。假设角色拥有一把单手剑,可能基础攻击力只有13,但是它有三个装备孔。当给剑镶嵌一颗蓝宝石的时候,它就拥有了额外的冰冻效果并多加2点攻击力;当给剑镶嵌一颗红宝石的时候,它又拥有了额外的火焰伤害并多加3点攻击力;当给剑镶嵌一颗绿宝石的时候,它又拥有了额外的中毒伤害并多加的4点攻击力。当然,也可以三个孔都镶嵌同一色的宝石。本文将说明如何使用Decorator模式来完成这样的设计。

使用继承来扩展

我们首先想到应该有个基类 Weapon,它供所有各式各样的武器继承,比如说Sword、Axe、Bow。Description字段代表武器的说明,比如“One-Hand light Sword”,Damage()方法则用于获取武器的伤害,GetDescription用于获取武器的说明。在不考虑宝石的情况下,我们得到下面的设计:

现在我们考虑如何创建镶嵌有宝石的武器。我们首先考虑到可以用继承来实现这样的设计,结果却发现如果我们需要定义所有嵌宝石的剑(Sword),就需要3+6+7 = 16个类(NOTE:三个物品孔,每个孔都有 蓝、红、绿 三种选择,可以两个或者三个孔同一色),如果我们给镶嵌了两颗蓝一颗红宝石的剑命名为 Blue2RedSword,给三色不同不剑命名为BlueRedGreenSword,其余的类推。那么,我们会得到下面这样庞大的类体系(只绘制了部分):

而这仅仅是开始,如果我们需要再添一种宝石,比如说白色,它可以附加诅咒的效果;或者我们需要给武器再添加一个物品孔,那么我们的类的数目将迅速的由十几个变成几十个。

我们发现使用继承的问题:使用继承时将会创建出大量的类。除此以外,使用继承,也意味我们需要实例化一个特定的子类以获取我们需要的功能(方法),这在编译阶段(compile time)就已经确定,类的客户端不能控制何时(run time)根据需要改变,除非再实例化另一个子类。

使用复合来扩展

我们发现继承会带来两个主要的问题,所以我们考虑换一种方式来思考,我们可以使用复合来完成它。说详细一点,就是我们将蓝宝石(BlueDiamond)、红宝石(RedDiamond)、绿宝石(GreenDiamond) 作为实体变量(instance variable)复合到基类中,然后在基类的Damage()方法中计算出所有宝石额外增加的伤害(此时基类的Damage()方法不再是抽象的)。

public abstract class Weapon{
    public virturl int Damage(){
       int total = 0;
       if(redDiamond!=null)
           total += redDiamond.Damage();       //附加红宝石的伤害
       if(blueDiamond!=null)
           total += blueDiamond.Damage();      //附加蓝宝石的伤害
       if(greenDiamond!=null)
           total += greenDiamond.Damage();     //附加绿宝石的伤害
       return total;
    }
}

而在实体子类中,我们覆盖这个方法,在方法内部先调用基类方法获取宝石的附加伤害,然后再给它加上武器本身的伤害。

public class Sword: Weapon{
    public override ind Damage(){
       return base.Damage() + 15; // 15 是剑本身伤害
    }
}

此时的图应该变成这样:

相对于继承,复合看上去要好得多,它的类的数目要少的多,并且又可以在运行时决定是否给武器镶嵌宝石,但是使用复合仍存在问题:

  • 宝石与剑是紧密耦合在一起的,当我们想要为武器添加一个白宝石,那么我们需要给Weapon基类再添加一个BlueDiamond字段,同时还需要修改基类的Damage()方法。简言之,每次维护我们都要修改以前的代码。
  1. 我们遗忘了一种组合,应该记得,我们的剑是可以镶嵌三个同色宝石的,比如说:三个蓝宝石或者 三个红宝石,那么上面的设计显然无法完成。当然,我们可以从三种宝石中抽象出一个Diamond基类来,而在Weapon中添加三个Diamond类型的变量。但是,问题依然存在:如果我们需要多添一个装备孔,那么我们又得再次修改Weapon类。

为对象添加状态和行为

现在假设我们不是一名软件设计者,而是一个游戏玩家,我们要为剑添加一枚红宝石,一枚蓝宝石,那么实际的操作顺序是什么呢?

  1. 我们当然首先要有一把剑。(需要先创建一个Sword对象,它只是把剑,不含任何宝石)。
  2. 我们为剑添加一个红宝石。(我们包装Sword对象,给它添加3点伤害,并给它火焰效果)。
  3. 我们为剑添加一个蓝宝石。(我们包装 包含了一个红宝石的Sword对象,给它添加2点伤害,并给它冰冻效果。)

用代码来体现应该就是这样的:

Weapon sword = new Sword();             // 创建一把剑
sword = new RedDiamond(sword);          // 给剑添加 红宝石
sword = new BlueDiamond(sword);         // 给剑添加 蓝宝石

从给剑添加红宝石那句代码,我们发现第一件事:宝石应该保存一个对剑的引用。然后我们就可以在宝石类的内部来为sword添加行为或状态。

从给剑添加蓝宝石那句代码,我们发现第二件事:添加了红宝石的剑(仅从代码看它属于是宝石),仍然是剑,所以我们得出:宝石应该和武器是同一个类型(Weapon基类)的,不然这里将无法再次传递。

这个过程用图来体现就是这样的:

如果我们将整个过程用UML来表示,就构成了下面这幅图:

从图中我们可以看到,通过宝石的扩展,我们可以为剑提供新的能力:额外的伤害加成,以及额外的武器特效(抱歉我不能显示一个华丽的魔法效果,只能在黑底白字的屏幕上输出一句:Addtional Effect: Fire !)。

在Damage() 和 GetDescription()中,我们先调用基类的相应方法,然后为Damage()添加来自宝石的额外的伤害(状态): iceDamage,以及来自宝石的额外效果(行为):FrozenEffect()。

Decorator 设计模式

上面这幅图,就构成了GOF的Decorator设计模式,我们现在看一下它的官方定义:动态地为对象添加额外的职能。Decorator模式为通过继承来为类扩展功能这种方式提供了另一种灵活的选择。

代码实现与测试

简单起见,我们只实现一种武器:Sword,两种宝石:蓝宝石 和 红宝石。

using System;
using System.Collections.Generic;
using System.Text;

namespace Decorator {

// 定义Weapon基类
    public abstract class Weapon {

protected string description;           // 武器的描述(攻击效果)

public virtual string GetDescription() {
           return description;
       }

public abstract int Damage();           // 武器的伤害
    }

// 定义剑
    public class Sword : Weapon {
       public Sword() {
           description = "One-Hand light Sword";
       }
       public override int Damage() {
           return 15;
       }
    }

// 定义宝石基类
    public abstract class Diamond : Weapon {
       protected Weapon weapon;        // 保存对武器的引用
    }

// 定义蓝宝石
    public class BlueDiamond : Diamond {
       private int iceDamage = 2;          // 蓝宝石的额外伤害

public BlueDiamond(Weapon weapon) {
           this.weapon = weapon;        // 保存引用
       }

public string IceEffect(){
           return "\nAddtional Effect: Frozen !";
       }

public override int Damage() {
           return weapon.Damage() + iceDamage; // 攻击加成
       }

public override string GetDescription() {
           return weapon.GetDescription() + IceEffect();  // 加入特殊攻击效果
       }
    }
   
    // 定义红宝石
    public class RedDiamond : Diamond {
       private int fireDamage = 3;         // 蓝宝石的额外伤害

public RedDiamond(Weapon weapon) {
           this.weapon = weapon;        // 保存引用
       }

public string FireEffect() {
           return "\nAddtional Effect: Fire !";
       }

public override int Damage() {
           return weapon.Damage() + fireDamage;    // 攻击加成
       }

public override string GetDescription() {
           return weapon.GetDescription() + FireEffect(); // 加入特殊攻击效果
       }
    }

class Program {
       static void Main(string[] args) {
           Weapon sword = new Sword();     // 创建一把新剑
           // 打印其描述(攻击效果) 和 伤害
           Console.WriteLine(sword.GetDescription() + "\nDamage:" + sword.Damage());
           Console.WriteLine();

sword = new BlueDiamond(sword); // 给剑添加一颗蓝宝石
           Console.WriteLine(sword.GetDescription() + "\nDamage:" + word.Damage());
           Console.WriteLine();

sword = new RedDiamond(sword);  // 给剑添加一颗红宝石
           Console.WriteLine(sword.GetDescription() + "\nDamage:" + sword.Damage());
           Console.WriteLine();
       }
    }
}

输出为:

One-Hand light Sword
Damage:15

One-Hand light Sword
Addtional Effect: Frozen !
Damage:17

One-Hand light Sword
Addtional Effect: Frozen !
Addtional Effect: Fire !
Damage:20

总结

本文中,我们通过一个常见的给武器(对象)添加宝石(额外的状态和行为)的例子,讨论了Decorator设计模式的实现过程。

我们首先提出了要解决的问题:给武器添加宝石,以使它有额外的攻击(伤害)加成和特殊的攻击效果。然后提出了使用继承会遇到的问题:大量的类以及只能通过实例化子类的方式获取行为。随后我们使用复合(Composition)的方式来解决,又遇到新的问题:程序不易维护,每次添加新的宝石或者添加新的物品孔,都需要修改代码。最后,我们使用Decorator模式巧妙地解决了这个问题。

希望这篇文章能给你带来帮助!

转载于:https://www.cnblogs.com/JimmyZhang/archive/2007/12/14/Decorator.html

奇幻RPG(物品锻造 与 Decorator模式)相关推荐

  1. 奇幻RPG(角色技能 与 Strategy模式)

    奇幻RPG(角色技能 与 Strategy模式) 引言 看过一些设计模式方面的书籍和文章,虽然很正式,很权威,(也觉得有那么一点刻板),总是觉得让人不那么好靠近.于是,我思考着像写故事一样来写下自己对 ...

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

    在暗黑破坏神等RPG游戏中,会遇到如下的一些场景:我有一把很普通的武器,我通过给它"注入魔法力量"."镶嵌宝石",来使得它拥有一些攻击特效:例如,一把普通长剑, ...

  3. 设计模式学习每天一个——Decorator模式

    参考 http://www.cnblogs.com/god_bless_you/archive/2010/06/10/1755212.html 一.装饰模式: 顾名思义,就是对物品进行装饰,既然是装饰 ...

  4. 结构型模式之Decorator模式

    1.意图 动态地给一个对象添加一些额外的职责.就增加功能来说,Decorator模式相比生成子类更为灵活 2.适用性 以下情况使用Decorator模式 (1)在不影响期货对象的情况下,以动态.透明的 ...

  5. 设计模式-Decorator模式

    目录 一个例子(贪玩蓝月) 传统继承实现 装饰器模式实现 对比 总结 Decorator(装饰器)模式属于结构型模式. 比如当其需要三种不同的附加特性,可以为其创建三个派生类.但是若它还需要同时具有其 ...

  6. 设计模式--装饰者(Decorator)模式

    模式定义 动态(组合)地给一个对象增加一些额外的职责,就增加功能而言,Decorator模式比生成子类(继承)更为灵活(消除重复代码并且减少子类个数) 类图 应用场景 扩展一个类的功能或给一个类添加附 ...

  7. 也说说“从Adapter模式到Decorator模式”

    为什么80%的码农都做不了架构师?>>>    终于有时间写点什么了,可以前酝酿好的东西似乎一下子都忘记了.这几天看了wayfarer的<<让僵冷的翅膀飞起来>系列 ...

  8. Java设计模式(7)装饰模式(Decorator模式)

    Decorator常被翻译成"装饰",我觉得翻译成"油漆工"更形象点,油漆工(decorator)是用来刷油漆的,那么被刷油漆的对象我们称decoratee.这 ...

  9. Decorator模式

    Decorator(装饰器)模式能够像标准的继承一样为类添加新的功能. 不同于标准继承机制的是,如果对象进行了实例化,Decorator模式能够在运行时动态地为对象添加新的功能. <?php a ...

最新文章

  1. python如何判断一段代码运行是否超出一定时间,如果超出则抛出异常?(检测函数运行是否超时,规定时间内执行,限制时间)eventlet模块 (eventlet.timeout.Timeout)
  2. boost::remove_vertex用法的测试程序
  3. linux安装dev命令,Linux安装与基础命令
  4. 用spring搭建微信公众号开发者模式下服务器处理用户消息的加密传输构架(java)
  5. Java—集合框架List
  6. carlife android 无线,carlife无线连接流程是什么
  7. 使用spring 配置数据源,并用数据源得到连接,操作sql
  8. 未在本地计算机上注册“Microsoft.ACE.OLEDB.12.0”提供程序搜索
  9. mysql 命令as 使用_【翻译自mos文章】使用asmcmd命令在本地和远程asm实例之间拷贝as...
  10. 【韩顺平 零基础30天学会Java】(第一阶段)(自用)
  11. matlab构建boost电路,基于matlab的boost电路仿真.doc
  12. 开启笔记本WIFI热点功能Microsoft Virtual WiFi Miniport Adapter
  13. 2021年山东省安全员C证考试题库及山东省安全员C证考试报名
  14. 利用DirectShow开发C#版的音频文件播放器(三)
  15. windows命令行工具(转)
  16. php下载到指定目录,下面这段代码如何下载到指定目录
  17. Python模拟post提交表单数据 ——某二手车网站回拨电话的分析与利用
  18. GitHub 优秀的开源项目学习
  19. qs美国排名计算机专业,2016年QS美国大学计算机科学专业排名
  20. 用Java整合Clamav病毒库检测病毒

热门文章

  1. kflash下载地址分享
  2. 网站信息收集工具RED_HAWK
  3. 5.10.1 操作查询之生成表查询
  4. Python 使用selenium爬取房天下网站,房源动态信息
  5. 深入浅出话多态(上)——具体而微
  6. deamon tools dtsoft virtual cdrom device 失败 错误
  7. 为外部硬件设置一键连接WiFi HF-SmartLink V7的使用
  8. 赚钱的B2C网站都有哪些特点
  9. 取经之路就在眼前--面经备战
  10. NeuralCF模型——CF与深度学习的结合