游戏代码浅读室/#1 建造者模式(Builder)
一、概述
本系列专栏为笔者学习体会随笔 没有绝对专业性,主要内容为对游戏设计模式及其他内容的理解和应用实例。
注:设计模式只是一种简单的设计思路,我们应该活用思路而不是按图索骥,适当修改能更好地应用于自己的游戏中。
在GoF(Gang of Four,四人组)的定义下,设计模式一共有23种常见的模式,这些设计模式分为三大类:创建型模式(Creational Pattern)、结构型模式(Structural Pattern)和行为型模式(Behavioral Pattern)。我们今天的主角就是创建型模式中的建造者模式(Builder Pattern)。
一般是这样定义建造者模式的:“将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示。”也就是说身为创建型的建造者模式是可以帮助我们去构建一个复杂的对象,并且将构建层与表示层的代码相分离 从而解耦代码,并使得这种构建方法可以广泛利用。这种描述难免不会让我们联想到另一种熟悉的创建型模式——工厂模式(在笔者的另一专栏中已介绍过),这两者的区别又是什么呢?
二、分析
学习过C#、Java等代码的读者们都应该知道,我们构建一个类或者成为对象的时候,会为了将其描述地具体生动赋予很多的变量 属性,并使用构造函数规范该对象被具体构建出来时候的构建流程。
例如,我们在RPG游戏中想要构建一个人物角色,基本上都会先建构一个基本的角色模板,并为他赋予外貌特征、性别、年龄等等属性,当我们想要创建一个具体人物的时候 就可以利用这个模板填入人物数据,从而创造一个真实的人物角色。
这个流程确实简单方便,但如果当我们想要构建的角色太过于复杂,需要很多的属性来让他在这个世界中更加真实 独特,这套流程往往会让我们的工作更加繁琐 枯燥 浪费时间。这时,建造者模式就可以来救场了!
也就是说:当我们需要构造一个构造参数数量超过4个的类,且部分参数是可选的时,考虑使用建造者模式。建造者模式是为构建复杂对象服务的。
常见的构造方法:
可以很明显看出,上述的创建方法在创建复杂角色中都缺乏可读性,并且属性一多起来就相当的混乱,这时我们可以使用 建造者模式(Builder)。
三、建造者模式
一般情况下,建造者模式中有四种角色:
1. 产品(Product):我们需要去构建的具体对象。
2. 抽象建造者(Builder):建造者的抽象基类(有时也会作为接口使用),定义了一个建造者的抽象方法,一般为构建步骤方法和产品获取方法。
3. 具体建造者(ConcreteBuilder):继承了Builder类或实现了IBuilder接口,实现了对象各个部件的构建方法和产品组装获取方法。
4. 指挥者(也叫导演,Director):可以视为管理类,是建造者系统与外部代码的连接桥梁,外部通过调用指挥者类中的Construct()方法来让建造者为我们构建对象。

四、动动手
让我们来使用建造者模式来创建一位RPG游戏中的角色。

首先,我们要先完成一个Product,这里安排是写一个Character作为基类,并依靠这个基类来创建不同职业的角色。
有了Product后,我们可以开始动手编写对应的抽象Builder了,在这段中我们的目的是定义这类Builder的基本功能。
接着我们来创建一个勇者职业的Builder类,之后我们想要创建一个勇者角色的时候就会使用这个Builder。
最终会由指挥者将所有的Builder统一管理,并与外部相交互,当调用传入的是HeroBuilder,那么Director就会自动为我们创建了一个勇者角色,而外部所要做的就是定义好角色的必要属性,勇者职业的特性都会由HeroBuilder帮助我们自动定义。
五、设计反思
建造者模式的优缺点:
优点:
建造者模式能够为我们提前封装好对应类型的相关细节,例如我们想要创建一个魔法师角色,那么就可以使用建造者模式提前为我们封装好魔法师角色的细节特性,外部就只需考虑该角色的基础面板。
每个Builder之间相互独立,我们可以自由地更换Builder来创建不同类型的角色,拓展性好。并且系统内部角色的创建过程是逻辑清晰的。
缺点:
建造者模式创建的对象一般具有较多的共同特点,也就是必要属性较多,如果不同类型的对象之间差异性过大,不建议使用建造者模式。
对象类型细分越多,我们就需要创建越多的Buidler,当我们的Builder类越来越多的时候,就会很难进行维护。
非必要属性的内容在Builder内部进行设定,没有参数引入,这是一个建造者模式的特点,但有些情况下这反而会让建造者模式不适合使用。
因此,一般情况下笔者的建议是尽量减少同类Builder之间的差异性。
