前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >RPG设计(物品锻造与Decorator模式)

RPG设计(物品锻造与Decorator模式)

作者头像
张子阳
发布于 2018-09-30 02:13:59
发布于 2018-09-30 02:13:59
2.1K2
举报

RPG设计(物品锻造与Decorator模式)

2007-12-14 作者: 张子阳 分类: 设计与模式

引言

物品锻造是各类奇幻游戏中的常见功能,就拿众所周知的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()方法不再是抽象的)。

代码语言:txt
AI代码解释
复制
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;
    }
}

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

代码语言:txt
AI代码解释
复制
public class Sword: Weapon{
    public override ind Damage(){
       return base.Damage() + 15; // 15 是剑本身伤害
    }
}

此时的图应该变成这样:

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

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

为对象添加状态和行为

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

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

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

代码语言:txt
AI代码解释
复制
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,两种宝石:蓝宝石 和 红宝石。

代码语言:txt
AI代码解释
复制
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();
       }
    }
}

输出为:

代码语言:txt
AI代码解释
复制
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模式巧妙地解决了这个问题。

感谢阅读,希望这篇文章能给你带来帮助!

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2007-12-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
2 条评论
热度
最新
如果想要把装上去的宝石卸载下来呢
如果想要把装上去的宝石卸载下来呢
11点赞举报
作者是在阐述设计模式 宝石卸载是关于数据的对象的!不在一个维度上
作者是在阐述设计模式 宝石卸载是关于数据的对象的!不在一个维度上
回复回复点赞举报
推荐阅读
编辑精选文章
换一批
装饰者模式Decorator
装饰者模式,动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更加有弹性的替代方案
见得乐
2022/07/20
6650
装饰者模式Decorator
RPG设计(角色技能与Strategy模式)
看过一些设计模式方面的书籍和文章,虽然很正式,很权威,(也觉得有那么一点刻板),总是觉得让人不那么好靠近。于是,我思考着像写故事一样来写下自己对设计模式的理解。我们将以一款奇幻角色扮演游戏(D&D)为蓝本,通过游戏中的模块创建或者功能实现来展示GOF的设计模式。当然,这不是一款真正意义上的游戏,只是为了了解设计模式,所以,我会尽可能的使游戏简单。废话不多说了,我们Start off吧。
张子阳
2018/09/30
2K0
RPG设计(角色技能与Strategy模式)
面向对象是什么
近两年设计了几个系统,不管是直接使用传统设计ER图,还是使用4C建模,但在做架构评审时,ER却都是重中之重,让人不得不深思,编程思想经过了一代代发展,为什么还在围绕ER,在远古时代,没有OO,没有DDD,但为什么延续至今的伟大软件也比比皆是
码农戏码
2021/03/23
6290
建模,没必要
Eric在DDD第一章节就介绍了模型,可见模型的作用不言而喻,说DDD是一种模型驱动设计方法,绝对没有问题
码农戏码
2022/03/11
4180
建模,没必要
java 常用十种设计模式示例归纳 | 已打包请带走
一个Demo,集合常用的十种设计模式,每个模式使用易被人们接受的案例讲述,按模式分包,使用设计模式前后对比,界面显示定义讲解,让你更深刻的了解每种设计模式。 大部分案例来自张鸿洋的博客。如有错误欢迎指正,如有侵权,请联系我删除。
Jingbin
2018/09/10
6.2K0
设计模式的征途—10.装饰(Decorator)模式
虽然目前房价依旧很高,就连我所在的成都郊区(非中心城区)的房价均价都早已破万,但却还是阻挡不了大家对新房的渴望和买房的热情。如果大家买的是清水房,那么无疑还有一项艰巨的任务在等着大家,那就是装修。对新房的装修并没有改变房屋用于居住的本质,但它可以让房子变得更加漂亮和温馨以及更加实用。在软件设计中,也有一种类似于新房装修的技术可以对已有的功能进行扩展使之更加符合用户需求,从而使得对象具有更加强大的功能,这便是本次即将介绍的装饰模式。
Edison Zhou
2018/08/20
5190
设计模式的征途—10.装饰(Decorator)模式
Java 设计模式:装饰者模式(Decorator Pattern)
装饰者模式属于结构型设计模式,允许通过动态包装对象的方式为对象添加新功能,提供比继承更灵活的扩展方式。该模式通过组合替代继承,遵循开闭原则(对扩展开放,对修改关闭)。
用户11531739
2025/03/14
480
装饰模式(Decorator)
装饰模式是一种结构型模式,允许你通过将对象放入包含行为的特殊封装对象中来为原对象绑定新的行为。
兜兜转转
2023/03/08
3680
装饰模式(Decorator)
设计模式 (三)——装饰者模式(Decorator,结构型)
使用设计模式可以提高代码的可复用性、可扩充性和可维护性。装饰者模式( Pattern)属于结构型模式,动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。它是通过创建一个包装对象,通过包装对象来包裹真实的对象,以达到装饰目的。
恋喵大鲤鱼
2018/08/03
8150
设计模式 (三)——装饰者模式(Decorator,结构型)
温故而知新:设计模式之装饰模式(Decorator)
小时候对日本的动画片十分着迷,“圣斗士”是我的最爱;长大后也曾经一度对“海贼王”十分痴迷;大学看武侠小说时,也特别喜欢那种主人公有奇遇的情况:吃到一颗千年异果,然后功夫大增60年... 这些个场景都有一个共同点:对象(或系统)会因为一些需求(通常这些需求之间没有任何关联),而扩展自己的功能。具体来说:青铜战士如果有幸能穿上黄金圣衣,不管你是不是黄金圣斗士,在穿上黄金圣衣的那一刻,你就具有黄金圣斗士的能力;海赋王中的人物,如果能吃到一颗奇异果,就能获得特别的能力(比如路飞就是吃了橡胶奇异果);武侠小说中,主
菩提树下的杨过
2018/01/22
4830
温故而知新:设计模式之装饰模式(Decorator)
C++设计模式——Decorator装饰器模式
装饰器模式是一种结构型设计模式, 它允许在不改变现有对象的情况下,动态地将功能添加到对象中。
Coder-ZZ
2024/06/18
2520
C++设计模式——Decorator装饰器模式
【愚公系列】2023年11月 二十三种设计模式(九)-装饰者模式(Decorator Pattern)
设计模式(Design Pattern)是软件开发领域的宝贵经验,是多人反复借鉴和广泛应用的代码设计指导。它们是一系列经过分类和归纳的代码组织方法,旨在实现可重用性、可维护性和可理解性。使用设计模式,我们能够编写高质量的代码,使其更易于他人理解,并提供了代码可靠性的保证。
愚公搬代码
2023/11/10
2510
【Java设计模式系列】装饰器模式(Decorator Pattern)
装饰模式以对用户透明的方式动态给一个对象附加功能。用户不会觉得对象在装饰前、后有何不同。装饰模式可在无需创造更多子类情况下,扩展对象的功能。
JavaEdge
2021/10/18
3600
设计模式- 装饰器模式(Decorator Pattern)
允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。 这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。
易兒善
2019/05/13
3170
Java 设计模式(五)《装饰器模式》
这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。
星尘的一个朋友
2020/11/25
5100
【愚公系列】2021年12月 二十三种设计模式(九)-装饰者模式(Decorator Pattern)
设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。项目中合理的运用设计模式可以完美的解决很多问题,每种模式在现在中都有相应的原理来与之对应,每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是它能被广泛应用的原因。
愚公搬代码
2022/12/01
1960
【愚公系列】2021年12月 二十三种设计模式(九)-装饰者模式(Decorator Pattern)
一起学习设计模式--10.装饰模式
尽管目前房价依然在涨,但依旧阻止不了大家对新房的渴望和买房的热情。如果大家买的是毛坯房,还要面临一个艰巨的任务,那就是装修。对于新房进行装修,并没有改变房屋用于居住的本质,但是它可以让房子变得更漂亮、更温馨、更实用、更能满足居家需求。在软件设计中,也有一种类似新房装修的技术可以对已有对象(新房)的功能进行扩展(装修),以获得更加符合用户需求的对象,使得对象具有更加强大的功能。这种技术对应于一种被称之为装饰模式的设计模式。
独立观察员
2022/12/06
5170
一起学习设计模式--10.装饰模式
设计模式之抽象工厂模式
抽象工厂模式(Abstract Factory)是一种创建型设计模式,提供一个接口用于创建一系列相关或相互依赖的对象,而无需指定它们的具体类。该模式的主要角色包括抽象工厂、具体工厂、抽象产品和具体产品。抽象工厂声明创建产品的方法,具体工厂实现这些方法生成具体的产品实例。抽象产品定义产品的接口,具体产品实现这些接口。此模式适用于需要创建一系列相关对象且系统需要独立于这些对象的创建和表示的场景。在实际应用中,抽象工厂模式可以用于跨平台UI组件库的设计,确保不同平台的UI组件风格一致。例如,通过不同的具体工厂创建
九转成圣
2024/06/03
2660
装饰器模式:如何用Java打扮一个对象?
在生活中,我们都知道一句话,“人靠衣装马靠鞍”,如果想要让自己在别人眼里看起来更加好看,更加丰富多彩,就得要学会打扮自己,为自己化妆,为自己穿好看的衣服,学会了打扮的本领,那么我们就可以轻松应对不同场合的需求。无论是日常通勤的简约风,还是晚宴的华丽造型,我们只需在“基础自我”上叠加不同的装饰元素,而无需改变本质——这种灵活性和可扩展性,恰恰是装饰器模式(Decorator Pattern) 在软件设计中的精髓。
Eulogy
2025/03/30
620
装饰器模式:如何用Java打扮一个对象?
Java 23种设计模式全归纳 | 已打包请带走
设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。
Jingbin
2020/02/13
7940
推荐阅读
相关推荐
装饰者模式Decorator
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档