您想找什么?
Hero background image
如何使用工厂模式在运行时创建对象

在您的 Unity 项目中实施常见的游戏编程设计模式可以帮助您高效地构建和维护一个简洁、有序和可读的代码库。设计模式减少了重构和测试时间,加快了开发进程,为游戏、团队和业务的发展奠定了坚实的基础。

不要把设计模式看成是可以复制并粘贴到代码中的成品解决方案,而应把它看成是可以帮助你构建更大的、可扩展的应用程序的额外工具。

本页介绍工厂设计模式。

这里的内容基于免费电子书、 用游戏编程模式提升代码水平.

Unity 最佳实践中心或通过这些链接查看Unity游戏编程设计模式系列中的更多文章:

工厂示意图
工厂设计模式为在超类中创建对象提供了一个接口,但允许子类改变将创建对象的类型
了解工厂模式

有时,一个特殊的对象可以创建其他对象,这很有帮助。许多游戏在游戏过程中会产生各种各样的东西,而在运行时你往往不知道自己需要什么,直到你真的需要它。

工厂模式为此指定了一个特殊的对象,你猜对了,就是工厂。从某种程度上说,它囊括了催生其 "产品 "的许多细节。这样做的直接好处是简化了代码。

不过,如果每个产品都遵循一个共同的接口或基类,就可以更进一步,使其包含更多自己的构造逻辑,将其从工厂本身中隐藏起来。这样,创建新对象的扩展性就更强了。

您还可以对工厂进行子类化,为特定产品创建多个专用工厂。这样做有助于在运行时生成敌人、障碍物或其他任何东西。

工厂样本项目
一种产品播放声音,另一种则播放粒子。两者使用相同的接口。
在示例项目中测试工厂模式

GitHub 上有一个示例项目,展示了游戏开发中的不同编程设计模式,包括工厂模式。

工厂模式示例包括让玩家在迷宫中移动的代码。在迷宫中,您可以通过点击产生两个不同的游戏对象,它们被称为产品。它们使用相同的界面,形状也相似,但一个会产生粒子,另一个会播放声音。

工厂模式场景位于标有 "6 工厂 "的文件夹中。

创建一个简单的工厂

想象一下,您想创建一个工厂模式,为游戏关卡实例化项目。您可以使用 Prefabs 创建 GameObject,但也可能希望在创建每个实例时运行一些自定义行为。

与其使用if语句或开关来维护这一逻辑,不如创建一个名为IProduct的接口和一个名为Factory的抽象类,如代码示例中所述。

产品的方法需要遵循特定的模板,但除此之外,它们并不共享任何功能。因此,您需要定义IProduct接口。

工厂可能需要一些共享的共同功能,因此本示例使用了抽象类。在使用子类时,请注意 SOLID 原理中的 Liskov 替换。它规定,超类的对象应该可以用子类的对象替换,而不影响程序的正确性。换句话说,任何使用超类引用的程序都应能在不知情的情况下使用其任何子类。

使用接口定义产品之间的共享属性和逻辑
使用接口定义产品之间的共享属性和逻辑
工厂模式中的类结构

IProduct接口定义了产品之间的共同点。在这种情况下,您只需拥有ProductName属性和产品在初始化时运行的任何逻辑。

然后,您可以根据需要定义任意多个产品 ProductAProductB等),只要它们遵循IProduct接口即可。

基类Factory 有一个GetProduct方法,可返回一个IProduct。它是抽象的,所以不能直接创建Factory的实例。您将派生出几个具体的子类(ConcreteFactoryAConcreteFactoryB),它们将实际获得不同的产品。

本例中的GetProduct将获取一个 Vector3 位置,这样就可以在特定位置更方便地实例化一个 Prefab GameObject。每个混凝土工厂中都有一个字段存储相应的模板预制件。

其结果就是类似上图的结构。

代码示例

在代码片段中,您可以看到示例ProductAConcreteFactoryA

在这里,您让实现IProduct的产品类 MonoBehaviours 利用了工厂中的 Prefabs。

请注意,每个产品都可以有自己的初始化版本。示例 ProductA Prefab 包含一个粒子系统(ParticleSystem),当 ConcreteFactoryA 实例化一个副本时,粒子系统就会发挥作用。工厂本身并不包含触发粒子的任何特定逻辑;它只调用所有产品通用的初始化方法。

浏览示例项目,了解ClickToCreate组件如何在工厂之间切换,以创建具有不同行为的 ProductA 和 ProductB。产品 B 在生成时会发出声音,而产品 A 则会产生粒子效果,以说明产品变体的核心概念。

优点和缺点

在设置许多产品时,工厂模式会让您受益匪浅。在应用程序中定义新的产品类型不会改变现有的产品类型,也不需要修改以前的代码。

将每个产品的内部逻辑分离到自己的类中,可使工厂代码相对简短。每个工厂只知道在每个产品上调用初始化,而不知道底层细节。

缺点是需要创建大量的类和子类来实现该模式。与其他模式一样,这也会带来一些开销,但如果产品种类不多,这可能是不必要的。另一方面,从长远来看,花费在设置类上的初始时间可能是一件好事,因为这样可以将代码解耦,使其更易于维护。

调整工厂模式

工厂的实现方式可能与这里所展示的大相径庭。在制作自己的工厂模式时,请考虑以下调整:

使用词典搜索产品: 您可能希望将产品作为键值对存储在字典中。使用唯一的字符串标识符(如名称或某个 ID)作为键,类型作为值。这样可以更方便地检索产品和/或其相应的工厂。

让工厂(或工厂经理)静止不动: 这使其更易于使用,但需要额外的设置。静态类不会出现在检查器中,因此您也需要将产品集合设置为静态。

将其应用于非游戏对象和非单体: 不要局限于预制件或其他团结体专用组件。工厂模式可用于任何 C# 对象。

与对象池模式相结合: 工厂并不一定需要实例化或创建新对象。它们还可以检索层级中的现有信息。如果要同时实例化许多对象(如武器的射弹),可使用对象池模式来优化内存管理。

工厂可根据需要催生任何游戏元素。然而,创造产品往往不是他们的唯一目的。您可能会将工厂模式作为其他更大型任务的一部分来使用(例如,在游戏关卡部分对话框中设置用户界面元素)。

博客文章
更多资源

有关如何在 Unity 应用程序中使用设计模式以及 SOLID 原则的更多提示,请参阅免费电子书《用游戏编程模式提升代码水平》。

您可以在最佳实践中心找到所有高级 Unity 技术电子书和文章。电子书籍也可在文档中的高级最佳实践页面获取。

您喜欢本文吗?
是的!
还行。