Unity 设计模式 之 创造型模式-【工厂方法模式】【抽象工厂模式】
目录
Unity 设计模式 之 创造型模式-【工厂方法模式】【抽象工厂模式】
一、简单介绍
二、工厂方法模式 (Factory Method Pattern)
1、什么时候使用工厂方法模式
2、使用工厂模式的好处
3、使用工厂方法模式时的注意事项
三、在 Unity 中使用 工厂方法 模式
1、 产品接口 (Shape)
2、具体产品类 (Cube 和 Sphere)
3、工厂接口 (ShapeFactory)
4、具体工厂类 (CubeFactory 和 SphereFactory)
5、客户端代码
6、实现总结
四、抽象工厂模式 (Abstract Factory Pattern)
1、什么时候使用抽象工厂模式
2、使用抽象工厂模式的好处
3、使用抽象工厂模式时的注意事项
五、在 Unity 中使用 抽象工厂 模式
1、抽象产品接口
2、具体产品类
3、抽象工厂接口
4、具体工厂类
5、客户端代码
6、实现总结
一、简单介绍
设计模式 是指在软件开发中为解决常见问题而总结出的一套 可复用的解决方案。这些模式是经过长期实践证明有效的 编程经验总结,并可以在不同的项目中复用。设计模式并不是代码片段,而是对常见问题的 抽象解决方案,它提供了代码结构和模块间交互的一种设计思路,帮助开发者解决特定的设计问题。
设计模式的特点:
- 通用性:设计模式针对的是软件开发中常见的设计问题,适用于各种软件工程项目。
- 可复用性:设计模式可以在不同项目和环境下被重复使用,提高代码的可维护性和扩展性。
- 可扩展性:设计模式有助于让代码结构更加灵活,易于扩展和修改。
- 模块化:通过设计模式,可以减少代码的耦合性,增强模块间的独立性。
- 提高沟通效率:设计模式为开发者提供了一种通用的设计语言,使得团队成员能够快速理解并讨论设计方案。
二、工厂方法模式 (Factory Method Pattern)
工厂方法模式 是一种创建型设计模式,用于定义一个用于创建对象的接口,但让子类决定实例化哪个具体类。它将 实例化对象的任务 推迟给子类,以解决创建对象时可能产生的耦合问题。
工厂方法模式的工作原理:
工厂方法模式的核心思想是 推迟实例化,将对象的创建延迟到子类中。通过定义一个抽象工厂类,客户端可以通过这个工厂接口生成产品对象,而不需要知道具体的类名,避免了直接使用
new
关键字创建对象。子类负责实现工厂方法,根据不同的业务需求来决定返回哪个产品对象。
工厂方法模式主要有以下几个组成部分:
- 产品接口(Product):定义所创建对象的通用接口。
- 具体产品类(ConcreteProduct):实现产品接口的具体类,表示不同的对象类型。
- 工厂接口(Creator):声明工厂方法,它返回产品接口的实例,通常是抽象类或接口。
- 具体工厂类(ConcreteCreator):实现工厂接口,并负责具体产品对象的实例化。
工厂方法模式的结构图
Product
:抽象产品ConcreteProduct
:具体产品Creator
:抽象工厂(包含工厂方法)ConcreteCreator
:具体工厂
classDiagram
class Creator {
<<abstract>>
+ factoryMethod() Product
}
class ConcreteCreator {
+ factoryMethod() ConcreteProduct
}
class Product {
<<abstract>>
}
class ConcreteProduct {
}
Creator <-- ConcreteCreator
Creator --> Product
ConcreteCreator --> ConcreteProduct
Product <|-- ConcreteProduct
1、什么时候使用工厂方法模式
对象创建过程复杂且可能发生变化时:
- 如果对象的创建过程涉及多个步骤,或者需要初始化大量参数,可以使用工厂方法模式将这些复杂的创建过程封装起来。当对象创建的逻辑变得更加复杂时,可以通过扩展工厂来简化代码维护。
当需要遵循开闭原则时:
- 如果系统需要根据不同的需求扩展不同的产品类,且不希望修改现有代码,工厂方法模式是理想选择。通过扩展新的具体工厂类,可以实现新的对象类型,而不影响现有系统。
系统中有许多类似的对象,且需要灵活的控制其创建过程时:
- 在某些情况下,系统中可能有多种不同的对象,每个对象都有各自的创建方式,使用工厂方法模式可以灵活地决定创建哪种类型的对象,而不必直接依赖于具体类。
当具体类的初始化工作依赖于外部资源时:
- 例如,在游戏开发中,工厂方法可以用来创建游戏对象,将资源加载、配置和初始化的过程封装在工厂中。
2、使用工厂模式的好处
解耦对象创建和使用:
- 工厂方法模式将对象的创建逻辑与业务逻辑分离,避免了在代码中直接实例化对象(使用
new
关键字),从而降低了代码的耦合性。提高代码的扩展性:
- 工厂方法模式符合开闭原则(OCP),当需要添加新产品时,不需要修改现有代码,只需增加新的具体产品类和对应的工厂实现。这使得系统在面对新需求时可以轻松扩展。
简化对象创建过程:
- 对于某些复杂对象的创建过程,工厂模式隐藏了这些细节,只需调用工厂方法即可获得对象实例。工厂封装了具体的创建逻辑,客户端不必关心对象的构造过程。
增强代码可维护性:
- 通过将对象创建逻辑封装在工厂中,所有对象的生成都集中管理。如果需要修改某类对象的创建过程,只需修改工厂类即可,不会影响到其他使用该对象的代码。
支持不同的对象实例化:
- 工厂方法模式允许在运行时决定生成何种对象实例,提供了更大的灵活性。例如,不同的子类可以根据业务逻辑创建不同的产品,而不用修改原有代码。
3、使用工厂方法模式时的注意事项
工厂类的数量增加:
- 如果系统中有很多不同的产品类型,会产生大量的具体工厂类,增加了类的数量。可以通过简单工厂模式或抽象工厂模式来减少工厂类数量。
增加了代码的复杂性:
- 工厂方法模式虽然提供了灵活性,但也增加了代码的复杂性,特别是在需要创建大量具体产品时,必须为每个产品创建单独的工厂类。
适合对象创建逻辑复杂的情况:
- 如果对象的创建过程非常简单,使用工厂方法模式可能会显得过于复杂,直接实例化对象反而更简洁明了。
三、在 Unity 中使用 工厂方法 模式
在 Unity 中使用 工厂方法模式 生成不同的3D对象(如立方体、球体)是一个非常实际的应用场景。Unity 提供了GameObject.CreatePrimitive()
方法来创建基本的3D对象。我们可以通过 工厂方法模式 封装创建逻辑,以实现更灵活的对象创建方式。
步骤:
- 定义产品接口:在这个例子中,
IShape
接口定义了所有形状的通用方法。- 具体产品类:实现
IShape
接口的具体类,如Cube
和Sphere
。- 工厂接口:定义创建形状的工厂方法。
- 具体工厂类:提供不同具体形状的工厂类,如
CubeFactory
和SphereFactory
。
参考类图如下:
1、 产品接口 (Shape)
首先,我们定义一个抽象类 Shape
,作为所有形状的基类。每个形状都有一个 Create()
方法,用于实例化具体的形状对象。
public abstract class AbShape
{
public abstract GameObject Create(Vector3 position, Color color);
}
2、具体产品类 (Cube 和 Sphere)
我们定义两个具体产品类 Cube
和 Sphere
,分别表示立方体和球体。
2.1 立方体类:Cube
public class Cube : AbShape
{
public override GameObject Create(Vector3 position, Color color)
{
GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube); // 创建立方体
cube.transform.position = position; // 设置位置
Renderer renderer = cube.GetComponent<Renderer>();
renderer.material.color = color; // 设置颜色
return cube;
}
}
2.2 球体类:Sphere
public class Sphere : AbShape
{
public override GameObject Create(Vector3 position, Color color)
{
GameObject sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere); // 创建球体
sphere.transform.position = position; // 设置位置
Renderer renderer = sphere.GetComponent<Renderer>();
renderer.material.color = color; // 设置颜色
return sphere;
}
}
3、工厂接口 (ShapeFactory)
定义一个抽象的工厂类 ShapeFactory
,它包含一个工厂方法 CreateShape()
,用于返回具体形状的对象。
public abstract class ShapeFactory
{
public abstract AbShape CreateShape();
}
4、具体工厂类 (CubeFactory 和 SphereFactory)
我们为每个具体的形状类创建对应的工厂类,CubeFactory
和 SphereFactory
,它们分别实现了 ShapeFactory
。
4.1 立方体工厂:CubeFactory
public class CubeFactory : ShapeFactory
{
public override AbShape CreateShape()
{
return new Cube(); // 返回立方体实例
}
}
4.2 球体工厂:SphereFactory
public class SphereFactory : ShapeFactory
{
public override AbShape CreateShape()
{
return new Sphere(); // 返回球体实例
}
}
5、客户端代码
在 GameManager
或任意脚本中调用工厂方法来动态创建对象。通过使用 CreateShape()
方法,客户端可以生成不同类型的 3D 对象,而无需直接依赖具体类。
public class ShapeGenerator : MonoBehaviour
{
private void Start()
{
// 创建 CubeFactory 实例并生成立方体
ShapeFactory cubeFactory = new CubeFactory();
AbShape cube = cubeFactory.CreateShape();
cube.Create(new Vector3(0, 0, 0), Color.red); // 在 (0, 0, 0) 位置生成红色立方体
// 创建 SphereFactory 实例并生成球体
ShapeFactory sphereFactory = new SphereFactory();
AbShape sphere = sphereFactory.CreateShape();
sphere.Create(new Vector3(2, 0, 0), Color.blue); // 在 (2, 0, 0) 位置生成蓝色球体
}
}
6、实现总结
- AbShape:抽象的产品类,定义形状的接口。
- Cube 和 Sphere:具体产品类,分别实现了立方体和球体的创建逻辑。
- ShapeFactory:抽象工厂类,定义了 CreateShape
()
方法。 - CubeFactory 和 SphereFactory:具体工厂类,用于创建相应的形状对象。
- ShapeGenerator:客户端,动态生成不同的 3D 对象。
通过工厂方法模式,Unity 的 3D 对象创建逻辑被很好地封装在工厂和产品类中,扩展新的 3D 对象也非常方便,比如可以添加更多的形状(如圆柱体、胶囊体等)而不需要修改现有代码。
四、抽象工厂模式 (Abstract Factory Pattern)
抽象工厂模式 (Abstract Factory Pattern) 是一种创建型设计模式,它提供了一个接口,允许客户端创建一系列相关或依赖的对象,而无需指定其具体类。抽象工厂模式通常用于创建一族相互关联的对象,并确保这些对象能够一起工作。
与 工厂方法模式 不同,抽象工厂不仅创建单个产品,还可以创建多个相关的产品对象。抽象工厂通过定义多个工厂方法,帮助客户端创建不同类型的相关产品。
抽象工厂模式的工作原理
- 抽象工厂定义了创建产品族的接口:该接口包含多个工厂方法,每个工厂方法用于创建一种产品。
- 具体工厂类实现这些工厂方法:每个具体工厂都负责创建一组相关的产品。它们返回具体产品的实例,并确保产品之间可以协同工作。
- 客户端只依赖于抽象工厂和抽象产品接口:客户端使用抽象工厂创建产品,而不需要了解具体产品类。通过依赖抽象接口,客户端可以灵活地使用不同的具体工厂来生成不同的一组产品。
抽象工厂模式的组成
- 抽象工厂(Abstract Factory):定义创建一组相关对象的方法。
- 具体工厂(Concrete Factory):实现抽象工厂接口,创建不同具体产品的实例。
- 抽象产品(Abstract Product):为每种类型的产品定义接口或抽象类。
- 具体产品(Concrete Product):实现抽象产品接口或继承抽象类,表示具体的产品对象。
- 客户端(Client):使用抽象工厂来创建一组产品,使用时只依赖于抽象工厂和抽象产品的接口。
抽象工厂模式的结构图
AbstractFactory
:抽象工厂ConcreteFactory
:具体工厂AbstractProductA
:抽象产品AConcreteProductA
:具体产品AAbstractProductB
:抽象产品BConcreteProductB
:具体产品B
classDiagram
class AbstractFactory {
<<abstract>>
+ CreateProductA() AbstractProductA
+ CreateProductB() AbstractProductB
}
class ConcreteFactory1 {
+ CreateProductA() ConcreteProductA1
+ CreateProductB() ConcreteProductB1
}
class ConcreteFactory2 {
+ CreateProductA() ConcreteProductA2
+ CreateProductB() ConcreteProductB2
}
class AbstractProductA {
<<abstract>>
}
class AbstractProductB {
<<abstract>>
}
class ConcreteProductA1 {
}
class ConcreteProductA2 {
}
class ConcreteProductB1 {
}
class ConcreteProductB2 {
}
AbstractFactory <|-- ConcreteFactory1
AbstractFactory <|-- ConcreteFactory2
AbstractFactory --> AbstractProductA
AbstractFactory --> AbstractProductB
AbstractProductA <|-- ConcreteProductA1
AbstractProductA <|-- ConcreteProductA2
AbstractProductB <|-- ConcreteProductB1
AbstractProductB <|-- ConcreteProductB2
1、什么时候使用抽象工厂模式
系统中有多个相关或依赖的产品需要创建时:
- 当系统需要创建多个相关对象,且这些对象可能属于不同的产品族时,使用抽象工厂模式可以有效地管理这些对象的创建。比如,创建不同风格的UI组件(按钮、文本框等)时,每个风格的组件需要一致性。
当产品的类型和数量会随着需求变化时:
- 如果产品种类较多且可能随着需求扩展,使用抽象工厂模式可以方便地增加新的产品族和产品,而无需修改客户端代码。
确保一系列对象能够一起使用时:
- 抽象工厂模式适合于需要保证一组对象能够协同工作、风格一致的场景。比如游戏中的不同敌人类型及其行为,或不同平台的UI组件库等。
需要遵循开闭原则时:
- 抽象工厂模式适用于那些需要经常扩展但不希望频繁修改代码的系统。通过新增具体工厂和产品,可以在不修改现有代码的前提下扩展系统功能。
2、使用抽象工厂模式的好处
提高代码的可扩展性:
- 抽象工厂模式使系统易于扩展,符合开闭原则。如果需要扩展新的产品族,只需创建新的具体工厂类和对应的产品类,而不需要修改现有代码。
解耦产品的创建和使用:
- 抽象工厂模式将产品的创建逻辑从客户端代码中移除,客户端不直接依赖具体类。这种解耦增强了代码的灵活性,使得客户端代码更容易维护和扩展。
确保产品族的一致性:
- 抽象工厂模式能确保创建的产品之间是相互兼容的。如果某个产品依赖于另一个产品(如UI组件的不同风格),抽象工厂可以保证这些相关产品能够协同工作。
简化对象创建过程:
- 对于涉及多个相关对象的复杂系统,抽象工厂模式可以简化对象的创建过程,避免了客户端需要分别创建不同对象的麻烦。
3、使用抽象工厂模式时的注意事项
抽象工厂模式适合用于管理一组相关对象:如果系统中的对象彼此相关(如UI组件、游戏角色等),抽象工厂模式可以简化管理这些对象的复杂性。
扩展产品族较为复杂:虽然增加新的产品类型很方便,但如果增加新的产品族,可能需要大量修改现有工厂。
保持产品族的一致性:在创建多个相关产品时,确保所有产品都来自同一个工厂,保持它们的一致性和兼容性。
五、在 Unity 中使用 抽象工厂 模式
在 Unity 中使用抽象工厂模式,我们可以根据时间的不同(如早晨、下午和晚上)来为 UI 元素(如按钮、文本框等)提供不同的颜色风格。我们可以为每个时间段设计不同的工厂,通过抽象工厂接口创建不同的按钮和文本框实例,并动态应用这些主题。
设计步骤
- 抽象产品:定义 UI 组件接口(如按钮、文本框等),并为每个组件实现
Render
方法。- 具体产品:根据不同的时间段(早晨、下午、晚上)实现不同颜色风格的按钮和文本框。
- 抽象工厂:定义一个抽象工厂接口,包含创建按钮和文本框的方法。
- 具体工厂:根据时间段实现具体的工厂类,负责创建对应时间段的 UI 组件。
- 客户端:根据当前时间,使用不同的工厂创建和渲染 UI。
参考类图如下:
1、抽象产品接口
// 抽象产品A - 按钮
public interface IButton
{
void Render();
}
// 抽象产品B - 文本框
public interface ITextBox
{
void Render();
}
2、具体产品类
// 早晨风格的按钮
public class MorningButton : IButton
{
public void Render()
{
Debug.Log("Rendering a Morning-style Button with light yellow.");
// 设置实际的 UI 样式或颜色
}
}
// 下午风格的按钮
public class AfternoonButton : IButton
{
public void Render()
{
Debug.Log("Rendering an Afternoon-style Button with warm orange.");
// 设置实际的 UI 样式或颜色
}
}
// 晚上风格的按钮
public class NightButton : IButton
{
public void Render()
{
Debug.Log("Rendering a Night-style Button with dark blue.");
// 设置实际的 UI 样式或颜色
}
}
// 早晨风格的文本框
public class MorningTextBox : ITextBox
{
public void Render()
{
Debug.Log("Rendering a Morning-style TextBox with light yellow background.");
// 设置实际的 UI 样式或颜色
}
}
// 下午风格的文本框
public class AfternoonTextBox : ITextBox
{
public void Render()
{
Debug.Log("Rendering an Afternoon-style TextBox with warm orange background.");
// 设置实际的 UI 样式或颜色
}
}
// 晚上风格的文本框
public class NightTextBox : ITextBox
{
public void Render()
{
Debug.Log("Rendering a Night-style TextBox with dark blue background.");
// 设置实际的 UI 样式或颜色
}
}
3、抽象工厂接口
// 抽象工厂接口
public interface IUIFactory
{
IButton CreateButton();
ITextBox CreateTextBox();
}
4、具体工厂类
// 早晨风格工厂
public class MorningUIFactory : IUIFactory
{
public IButton CreateButton()
{
return new MorningButton();
}
public ITextBox CreateTextBox()
{
return new MorningTextBox();
}
}
// 下午风格工厂
public class AfternoonUIFactory : IUIFactory
{
public IButton CreateButton()
{
return new AfternoonButton();
}
public ITextBox CreateTextBox()
{
return new AfternoonTextBox();
}
}
// 晚上风格工厂
public class NightUIFactory : IUIFactory
{
public IButton CreateButton()
{
return new NightButton();
}
public ITextBox CreateTextBox()
{
return new NightTextBox();
}
}
5、客户端代码
public class UIDisplay : MonoBehaviour
{
private IUIFactory uiFactory;
private void Start()
{
// 根据当前时间获取对应的工厂
uiFactory = GetFactoryByTime();
// 使用工厂创建并渲染UI组件
IButton button = uiFactory.CreateButton();
ITextBox textBox = uiFactory.CreateTextBox();
button.Render();
textBox.Render();
}
private IUIFactory GetFactoryByTime()
{
// 获取当前时间的小时
int hour = System.DateTime.Now.Hour;
if (hour >= 6 && hour < 12)
{
// 早晨6点到12点
return new MorningUIFactory();
}
else if (hour >= 12 && hour < 18)
{
// 下午12点到18点
return new AfternoonUIFactory();
}
else
{
// 晚上18点到6点
return new NightUIFactory();
}
}
}
6、实现总结
- 抽象产品接口 (
IButton
和ITextBox
):定义了通用的 UI 组件方法Render
,用于在游戏中展示 UI 元素。- 具体产品类 (
MorningButton
,AfternoonButton
,NightButton
等):这些类实现了具体的按钮和文本框,根据不同的时间段来渲染不同的颜色风格。- 抽象工厂接口 (
IUIFactory
):定义了用于创建不同 UI 元素的方法,如按钮和文本框。- 具体工厂类 (
MorningUIFactory
,AfternoonUIFactory
,NightUIFactory
):分别根据早晨、下午和晚上的不同,创建相应颜色风格的按钮和文本框。- 客户端 (
UIDisplay
):根据当前时间,动态选择对应的工厂生成 UI 元素,并调用Render
方法进行渲染。
解耦创建和使用,UI 元素的创建与其使用分离,客户端代码不需要关心具体的 UI 组件实现。
灵活应对时间变化,客户端可以根据当前时间灵活选择不同的工厂,动态生成适合的 UI 主题。
易于扩展,如果需要支持新的时间段(例如黄昏、黎明),可以方便地添加新的具体工厂和产品,而无需修改客户端代码。