`

【转】奇幻RPG(人物构造 与 Abstract Factory模式)

 
阅读更多

http://www.tracefact.net/Design-Pattern/Abstract-Factory.aspx

 

引言

在前一节,我们介绍了Strategy模式,并使用此模式实现了一个根据角色的职业来分配技能的范例(实际也就是动态地为类分配方法)。作为一款奇幻RPG,有了职业,我们还应当可以为角色选择种族,比如说:人类(Human)、精灵(Elf)、矮人(Dwarf)、兽人(Orc)等等。而这四个种族又有着截然不同的外形,精灵皮肤灰白、有着长长的耳朵、没有体毛和胡须;矮人的皮肤与人类近似,但是身材矮小、通常留着浓密的胡子;兽人则有着绿色的皮肤和高大的身躯,并且面目丑陋。本文将讨论如何使用GOF的Abstract Factory抽象工厂来实现这样的角色外形设计。

面向实现的方式

简单起见,我们假设角色身体由三部分构成,分别是:头(Head)、身材(Stature)、皮肤(Skin)。那么对于人类的构造,我们的第一反应自然而然地想到:它应当由 HumanHead、HumanStature、HumanSkin三个类复合而成。对于精灵也是类似的设计,于是,我们实现了下面这样的设计(本文将仅以人类和精灵为例介绍):

抽象组成身体的实体类

我们发现这样做,每个角色与他的身体部件是牢牢绑定在一起的,每创建一个角色,我们都需要为它先行创建所有其复合的类(组成身体的实体类(Concret Class),比如HumanHead)。按照面向对象的思想,我们想到应该对这一过程进行封装,将创建角色部件这件事委派给其他的类来完成。观察上图,我们发现尽管角色不同,但它们都是由三个部分构成,所以,我们所能想到的实现这一过程的第一步,就是对组成身体的实体类进行抽象,我们定义三个接口:Head、Stature、Skin,代表身体的三个部分,并且让Human和Elf的实体类去实现这个接口:

观察上图,我们发现尽管定义了接口,但是如果角色Human和Elf仍然与接口的实体类关联,那么效果与面向实现完全相同。现在,是时候对设计做些改动,我们让Human和Elf与接口关联,而不是与接口的实现关联(OO思想:面向接口编程,而不是面向实现编程)。这一次,我们的设计变成下图:

一眼望去,我们发现的第一个问题就是:Human与Elf惊人地相似,再仔细看看,我们注意到它们除了名字不同其余的完全一样。我们不禁思考:有必要把两个完全一样的类起两个名字分别保存么?答案是没有必要,我们将它们合并成一个类,起名叫Race,设计再次变成下面这样:

创建工厂类

看到这里,我们可能会想:现在结构似乎已经很完善了,我们定义了接口来解决问题,也没有为不同的角色创建多个不同的类,而只要在Race的构造函数中为代表身体部件的变量赋不同的值,就可以创建不同种族的角色。好的,那么我们来看看如果要创建一个Human 代码需要如何编写:

Head head = new HumanHead();
Stature stature = new HumanStature();
Skin skin = new HumanSkin();
Race human = new Race(head, stature, skin);

而Race的构造函数是这样的:

public Race(head, stature, skin){
    this.head = head;
    this.stature = stature;
    this.skin = skin;
}

我们看到,仅仅创建一个类这样似乎太麻烦了,而且身体的部分类是角色的组成部分,为什么它们要先于角色创建呢?

这时候,我们想到如果有一个类可以专门负责创建身体部件这件事,当我们想要创建角色的时候,将这个类传递给Race的构造函数就可以了。我们管创建Human身体组成部分的类称作:HumanPartsFactory,创建Elf身体部分的类称作ElfPartsFacotry。那么它们应该是这样的:

现在,我们再要创建一个Human,代码变成了这样:

HumanPartsFactory humanFactory = new HumanPartsFactory();
Race Human = new Race(humanFactory);

相应的,我们的构造函数也需要改一改:

public Race(HumanPartsFactory humanFacotry){
    head = humanFactory.CreateHead();
    stature = humanFactory.CreateStature();
    skin = humanFactory.CreateSkin();
}

一切似乎都很好,直到我们需要创建一个Elf的时候... 我们发现Race的构造函数只能接受一个HumanPartsFactory类型的参数,为了传递ElfPartsFactory,我们将不得不再添加一个接受ElfPartsFactory类型的构造函数。这样显然不好,这一次,有了之前的经验,我们知道我们可以通过同样的方法来解决。

看到这里,你是否能够体会到一些“面向接口”编程的意味?注意到RacePartsFactory,它内部的方法返回的都是接口类型,而其实体子类的方法返回的都是接口的实体类。如果我们之前不声明那看似无用的接口,这里是无法实现的。

现在,我们再次修改Race的构造函数:

public Race(RacePartsFactory raceFacotry){
    head = raceFacotry.CreateHead();
    stature = raceFacotry.CreateStature();
    skin = raceFacotry.CreateSkin();
}

当我们需要一个Human的时候:

RacePartsFactory humanFactory = new HumanPartsFactory();
Race human = new Race(humanFactory);

当我们需要一个Elf的时候:

RacePartsFactory elfFactory = new ElfPartsFactory();
Race elf = new Race(elfFactory);

Abstract Factory设计模式

上面做的这些,使我们又完成了一个设计模式:Abstract Factory。它的正式定义是这样的:提供一个接口用于创建一系列相互关联或者相互依赖的对象,而不需要指定它们的实体类。

下面是本例中 Abstract Factory模式的最终图:

代码实现和测试

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

namespace AbstractFactory {

    // 定义构成身体部分的接口
    public interface IHead {  string name { get;} }
    public interface IStature {  string name { get;} }
    public interface ISkin {  string name { get;} } 

    // 组成 Human 的类
    public class HumanHead : IHead { public string name { get { return "Human Head"; } } }
    public class HumanStature : IStature { public string name { get { return "Human Stature"; } } }
    public class HumanSkin : ISkin { public string name { get { return "Human Skin"; } } }

    // 组成 Elf 的类
   public class ElfHead : IHead { public string name { get { return "Elf Head"; } } }
    public class ElfStature : IStature { public string name { get { return "Elf Stature"; } } }
   public class ElfSkin : ISkin { public string name { get { return "Elf Skin"; } } }

    // 定义工厂接口
    public interface IRacePartsFactory {
       IHead CreateHead();
       ISkin CreateSkin();
       IStature CreateStature();
    }

    // 定义Human身体的工厂类
    public class HumanPartsFactory : IRacePartsFactory {
       public IHead CreateHead() {
           return new HumanHead();
       }
       public IStature CreateStature() {
           return new HumanStature();
       }
        public ISkin CreateSkin() {
           return new HumanSkin();
       }
    }

    // 定义Elf身体的工厂类
    public class ElfPartsFactory : IRacePartsFactory {
       public IHead CreateHead() {
           return new ElfHead();
       }
       public IStature CreateStature() {
           return new ElfStature();
        }
       public ISkin CreateSkin() {
           return new ElfSkin();
       }
    }
    
    // 定义 Race 类
    public class Race {
       public IHead Head;    // 做示范用,所以没有构建属性
       public IStature Stature;
       public ISkin Skin;

       public Race(IRacePartsFactory raceFactory) {
           Head = raceFactory.CreateHead();
           Stature = raceFactory.CreateStature();
           Skin = raceFactory.CreateSkin();
       }
    }

    class Program {
       static void Main(string[] args) {
           // 创建一个 精灵(Elf)
           Race Elf = new Race(new HumanPartsFactory());
           Console.WriteLine(Elf.Head.name);
           Console.WriteLine(Elf.Stature.name);           
           Console.WriteLine(Elf.Skin.name);
       }
    }
}

总结

本文中我们一步步学习了Abstract Factory抽象工厂模式的实现。

我首先介绍了我们奇幻RPG所面临的一个问题:我们需要创建形态各异的角色。随后,我们通过面向实现的方式来完成了这一过程,并讨论了它的不足。随后,我们先通过接口的使用对种族进行了抽象。接着,我们由将创建角色组成部分类的过程进行了封装,将这一过程委派给了工厂类。最后,我们又对工厂类进行了抽象,最终实现了Abstract Factory工厂模式。

希望本文能给你带来帮助。

分享到:
评论

相关推荐

    设计模式之Abstract Factory模式和代码实现

    在设计模式中,Abstract Factory模式是一种用来解决在不指定具体实现的情况下创建一系列相关或相互依赖对象的模式。在本文的上下文中,我们看到它被应用于一个奇幻RPG游戏的角色种族设计,每个种族(如人类、精灵)...

    rpg maker xp 素材

    《RPG Maker XP 素材详解:打造属于你的奇幻世界》 RPG Maker XP 是一款深受游戏开发者喜爱的工具,它允许用户无需编程知识就能创建自己的角色扮演游戏。这款软件的强大之处在于它丰富的素材库,使得游戏制作变得...

    Fantasy RPG Icon Pack 2.zip

    《奇幻RPG图标包2》是一款专为角色扮演游戏(RPG)设计的图标集合,包含了一系列精心绘制的图形资源,适用于游戏开发、UI设计、视觉艺术创作等多个领域。这个图标包以其奇幻主题为特色,提供了丰富的视觉元素,帮助...

    dev C 奇幻方程序

    女儿编的第一个c程序,奇幻方的构造,打印。

    整理的几个RPG游戏技能特效

    总之,Unity3D的粒子系统为RPG游戏的技能特效提供了无限可能,无论是物理攻击的现实感还是魔法攻击的奇幻感,都能通过精心设计的粒子效果来生动展现。开发者可以根据需求,利用这些工具和资源,打造出独特且引人入胜...

    RPG游戏引擎设计原理

    RPG 是深受广大游戏迷们喜爱的一种游戏 它以独特的互动性和故事性吸引了无数的玩家 它向人们提供了超出现实生活的广阔的虚拟世界 使人们能够尝试扮演不同的角色 去经历和体验各种不同的人生旅程或奇幻经历 ...

    RPG游戏经典策划案例

    在游戏行业中,RPG(Role-Playing Game,角色扮演游戏)是一种深受玩家喜爱的游戏类型,它允许玩家扮演虚构世界中的角色,通过互动、探索和决策来推进故事和游戏进程。"RPG游戏经典策划案例"是一个关于如何规划和...

    设计模式之 Decorator模式和代码实现

    在上述的奇幻RPG游戏中,Decorator模式被用来实现武器的锻造过程,尤其是武器镶嵌宝石的功能。这个过程涉及到对基础武器(如剑)的扩展,以增加不同的属性和效果。 在没有引入Decorator模式之前,我们可能会使用...

    The Fairy Tale REVAMPED:奇幻RPG游戏-开源

    本项目,名为"The Fairy Tale REVAMPED",是一个开源的奇幻角色扮演游戏(RPG),将经典童话元素与现代游戏设计相结合,为玩家带来一个全新的冒险体验。游戏背景设定在一个名为潘塔萨尔的世界,这个世界在过去的一万...

    Epic RPG Equipment Icon Set

    此外,图标的设计还可能考虑到游戏的整体艺术风格,例如,如果是一款奇幻风格的RPG,那么图标可能会带有龙、精灵等神话元素;如果是科幻风格,那么可能会融入未来科技的元素。 《Epic RPG Equipment Icon Set》的...

    jQuery picEyes ,全屏模式下奇幻缩放

    jQuery picEyes ,全屏模式下奇幻缩放,实现点击图片放大,jQuery picEyes ,全屏模式下奇幻缩放,实现点击图片放大,jQuery picEyes ,全屏模式下奇幻缩放,实现点击图片放大,

    Big Fantasy RPG Music Bundle 1.0

    奇幻RPG音乐合辑-史诗之旅 幻想RPG音乐合辑-史诗之旅2 奇幻RPG音乐合辑-《失落的王国》 风景 - 环境探索音乐合辑 +7首附加曲目 超过2小时的高品质音乐,45首曲目分为6类:主题,探索城镇和城堡,探索风景,探索地牢...

    RPG Dreamer制作的视觉小说游戏-仙剑奇侠转

    在制作《仙剑奇侠传》时,我们需根据原作的角色设定,设计出符合人物性格特征的形象,同时考虑场景的细节,如古色古香的城镇、神秘的仙灵之地,确保视觉效果与故事背景相吻合。 接下来是剧情构建。视觉小说的核心...

    奇幻变脸秀(fantamorph deluxe) v5.8.0.6 绿色破解版

    创作一部自己的变形特效影片, 并与亲友分享, 将带给你奇幻有趣的体验! 奇幻变脸秀5.0版十大新功能 新增变形控制工具: 镜头 通过可自由设定的曲线来控制画面的平移/缩放/旋转. 仅用于豪华版中. 新增变形控制工具: ...

    DrdSim:奇幻 RPG 游戏的可编程平台。-开源

    《DrdSim:开源奇幻RPG游戏开发平台详解》 DrdSim是一个专注于奇幻角色扮演游戏(RPG)的可编程平台,它源自ALTAR(tm)出版的“Draci Doupe”游戏,为开发者和游戏爱好者提供了丰富的创作工具。作为一个开源项目,...

    RPG东方资源

    在"RPG东方资源"中,图标可能包括但不限于角色头像、道具图标、魔法效果图标、敌人形象等,这些都与东方系列的风格和主题保持一致。东方系列是一个知名的弹幕射击游戏系列,以其独特的角色设计、丰富的背景故事和...

    RollPlay.rar_RPG_rpg游戏_漂亮

    《RollPlay.rar》是一款以RPG(角色...总的来说,《RollPlay.rar》是一个集角色成长、策略战斗和精美画面于一身的RPG游戏,它将带领玩家进入一个充满奇幻色彩的世界,通过不断的探索和战斗,体验一场难忘的冒险之旅。

    全新原创ppt奇幻异型图表

    全新原创ppt奇幻异型图表

    compworlds_rpg:一个简短的 2D 奇幻 RPG

    注意:目前 2.0 分支的工作正在进行中。 目前还没有 ETA 发布,但核心相当稳定,并且已经在大量生产中使用。 。描述是现代网络的音频库。 它默认为并回退到 。 更多文档、示例和演示可以在上找到。...

    The Endless Abyss:带有动作战斗系统的 2D 奇幻 RPG-开源

    《无尽深渊》讲述了一个战士和一个需要探索和捍卫的世界的故事。 在这个动作角色扮演游戏冒险中,当玩家穿越被称为大陆的土地的所有四个角落时,扮演野兽、巫师和强盗。 利用剑、咒语、弓和敏捷的手指来维持生命并将...

Global site tag (gtag.js) - Google Analytics