Unity 设计模式 之 创建型模式 -【单例模式】【原型模式】 【建造者模式】

Unity 设计模式 之 创建型模式 -【单例模式】【原型模式】 【建造者模式】

目录

Unity 设计模式 之 创建型模式 -【单例模式】【原型模式】 【建造者模式】

一、简单介绍

二、单例模式 (Singleton Pattern)

1、什么时候使用单例模式

2、单例模式的好处

3、使用单例模式的注意事项

三、在 Unity 中使用 单例模式

1、普通型(new)泛型单例模式

2、继承 MonoBehaviour 的泛型单例模式

3、单例使用方式

4、实现分析

四、原型模式 (Prototype Pattern)

1、原型模式的原理

2、什么时候使用原型模式

3、使用原型模式的好处

4、使用原型模式的注意事项

五、在 Unity 中使用 原型模式

1、 定义基类 ShapePrototype

2、创建具体的形状类

3、创建 ShapeSpawner 负责克隆对象

4、设置场景中的原型对象

5、运行效果

六、建造者模式 (Builder Pattern)

1、什么时候使用建造者模式

2、使用建造者模式的好处

3、建造者模式的注意事项

七、在 Unity 中使用 建造者模式

1、定义房屋类 House

2、定义建造者接口 IHouseBuilder

3、 具体建造者类:木屋建造者 WoodenHouseBuilder 和砖屋建造者 BrickHouseBuilder

4、定义指挥者 Director

5、 在 Unity 场景中使用建造者模式

6、运行效果


一、简单介绍

设计模式 是指在软件开发中为解决常见问题而总结出的一套 可复用的解决方案。这些模式是经过长期实践证明有效的 编程经验总结,并可以在不同的项目中复用。设计模式并不是代码片段,而是对常见问题的 抽象解决方案,它提供了代码结构和模块间交互的一种设计思路,帮助开发者解决特定的设计问题。

设计模式的特点:

  1. 通用性:设计模式针对的是软件开发中常见的设计问题,适用于各种软件工程项目。
  2. 可复用性:设计模式可以在不同项目和环境下被重复使用,提高代码的可维护性和扩展性。
  3. 可扩展性:设计模式有助于让代码结构更加灵活,易于扩展和修改。
  4. 模块化:通过设计模式,可以减少代码的耦合性,增强模块间的独立性。
  5. 提高沟通效率:设计模式为开发者提供了一种通用的设计语言,使得团队成员能够快速理解并讨论设计方案。

二、单例模式 (Singleton Pattern)

单例模式 (Singleton Pattern) 是一种创建型设计模式,保证一个类只有一个实例,并提供一个全局访问点来获取该实例。它通过控制类的实例化过程,确保系统中只有一个该类的对象存在。

在单例模式中,类的构造函数通常是私有的,防止外部通过 new 来创建对象,类内部维护一个静态实例,通过公共的静态方法提供访问。

单例模式的特点

  1. 唯一实例:单例模式确保一个类只有一个实例,无论系统中的组件如何调用它。
  2. 全局访问点:单例模式为客户端提供一个全局访问点(通常是静态方法),通过这个访问点,所有客户端都能获得相同的实例。
  3. 延迟初始化:可以在首次访问时创建该实例,节省系统资源(可选)。

1、什么时候使用单例模式

  1. 全局唯一的资源管理:如果一个类需要控制对某种共享资源(如数据库连接池、文件系统、日志器、配置管理器等)的访问,并且不允许有多个实例同时存在。

  2. 配置或状态共享:当系统中某个类需要持有全局配置或共享状态时,单例模式可以使得这些状态在不同模块中保持一致。

  3. 资源开销较大的对象:如果一个类的实例化代价较高(如外部资源初始化、复杂运算等),而且只需要一个实例时,单例模式可以避免多次重复的创建操作。

  4. 需要严格控制实例数量:在某些业务逻辑中,严格要求一个类只能有一个实例,否则会引发逻辑混乱或资源竞争(如操作系统中的任务管理器)。

2、单例模式的好处

  1. 控制实例数量:确保一个类只生成一个实例,避免不必要的内存占用,尤其对于管理全局状态或资源的对象(如数据库连接、日志系统、配置管理等),控制实例数量可以提高系统的稳定性。

  2. 全局访问:单例模式提供了一个全局访问点,这使得某些系统级别的服务(如日志系统、资源管理器等)能够方便地被全局访问。

  3. 节省资源:避免重复创建相同对象,减少了资源的消耗,尤其是对于耗费较大的对象(如文件系统、数据库连接等)。

  4. 线程安全:通过实现线程安全的单例,可以确保在多线程环境下,多个线程对该对象的操作是安全的。

3、使用单例模式的注意事项

  1. 全局状态的复杂性:虽然单例模式提供全局访问,但这也意味着它引入了全局状态,可能导致系统的状态管理变得复杂,特别是在大型系统中。

  2. 并发和线程安全:在多线程环境下,需要确保单例的实现是线程安全的,否则可能会出现多个线程同时创建实例的情况,导致数据不一致。

  3. 难以扩展:由于单例模式限制了对象的创建数量,在某些情况下,可能不利于系统的扩展或测试。比如,在单元测试中,单例模式可能难以进行模拟。

  4. 滥用风险:单例模式提供全局访问点,容易被滥用为全局变量,违背面向对象设计中高内聚、低耦合的原则。因此,使用时应考虑是否真的有必要将类设计为单例。

总之,单例模式 主要用于控制对象的实例化,确保系统中只有一个类的实例,并通过全局访问点来控制对象的使用。它适用于需要全局共享资源、统一管理的场景,如日志系统、数据库连接池等。尽管单例模式在某些场景下有助于提升系统的稳定性和效率,但也应谨慎使用,以避免全局状态管理复杂化或滥用全局访问带来的耦合问题。

三、在 Unity 中使用 单例模式

在 Unity 中,实现一个线程安全的普通类MonoBehaviour 类的泛型单例时,必须考虑以下几点:

  1. 普通类单例:不能被 new,并且在多线程环境下线程安全。
  2. MonoBehaviour 单例:由于 MonoBehaviour 的实例是通过 Unity 的 AddComponent 创建的,不能直接通过 new,也需要保证在多线程环境下的安全性。

参考类图如下:

1、普通型(new)泛型单例模式

方法一:
public abstract class Singleton<T> where T : class, new()
{
    private static T instance = null;
 
    // 多线程安全机制
    private static readonly object locker = new object();
 
    public static T Instance
    {
        get
        {
            lock (locker)
            {
                if (instance == null)
                    instance = new T();
                return instance;
            }
        }
    }
}
 
 
 
方法二:
 
using System;
using System.Reflection;
 
/// <summary>
/// 单例
/// 继承需要一个非公有的无参构造函数
/// </summary>
/// <typeparam name="T">类名的泛型</typeparam>
public abstract class Singleton<T> where T : class
{
    private static T instance = null;
 
    // 多线程安全机制
    private static readonly object locker = new object();
 
    public static T Instance
    {
        get
        {
            // 线程锁
            lock (locker)
            {
                if (null == instance)
                {
                    // 反射获取实例
                    var octors = typeof(T).GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic) ;
 
                    // 获取无参数的非公共构造函数
                    var octor = Array.Find(octors, c => c.GetParameters().Length == 0);
 
                    // 没有则提示没有私有的构造函数
                    if (null == octor)
                    {
                        throw new Exception("No NonPublic constructor without 0 parameter");
                    }
 
                    // 实例化
                    instance = octor.Invoke(null) as T;
                }
 
                return instance;
 
            }
        }
    }
 
    /// <summary>
    /// 构造函数
    /// 避免外界new
    /// </summary>
    protected Singleton() { }
}

2、继承 MonoBehaviour 的泛型单例模式

using UnityEngine;
 
public abstract class MonoSingleton<T> : MonoBehaviour where T : MonoBehaviour
{
    private static T instance = null;
 
    private static readonly object locker = new object();
 
    pri
### Unity 中实现单例模式的方法 在 Unity 开发中,单例模式是一种常见的设计模式,用于确保某个类只有一个实并提供全局访问点。以下是几种常见的方式以及它们的优缺点。 --- #### 方法一:基于 `MonoBehaviour` 的简单例模式 这种方法适用于需要继承自 `MonoBehaviour` 的类,并且可以在场景之间保持状态不变的情况。 ```csharp public class MySingleton : MonoBehaviour { private static MySingleton instance; public static MySingleton Instance { get { if (instance == null) { instance = FindObjectOfType<MySingleton>(); if (instance == null) { instance = new GameObject("MySingleton").AddComponent<MySingleton>(); DontDestroyOnLoad(instance.gameObject); } } return instance; } } private void Awake() { if (instance != null && instance != this) { Destroy(gameObject); } else { instance = this; DontDestroyOnLoad(gameObject); } } } ``` 此方法通过静态属性 `Instance` 提供全局访问接口,并利用 `Awake()` 函数来确保同一时间只存在一个实[^2]。 --- #### 方法二:泛型单例模式 对于不依赖于 `MonoBehaviour` 的类,可以使用泛型方式实现单例模式。这种方式更加通用,适合纯逻辑类。 ```csharp using System; public class Singleton<T> where T : class, new() { private static readonly Lazy<T> lazyInstance = new Lazy<T>(() => new T()); public static T Instance => lazyInstance.Value; protected Singleton() { } } // 使用示 public class GameManager : Singleton<GameManager> { public void SomeMethod() { Console.WriteLine("GameManager Method Called"); } } ``` 上述代码定义了一个泛型基类 `Singleton<T>`,并通过子类 `GameManager` 来调用其唯一实[^5]。 --- #### 方法三:带自动销毁机制的单例模式 为了防止重复创建对象,可以通过检测当前场景是否存在该来进行优化。 ```csharp public class PlayerSingletonMono : MonoBehaviour { private static PlayerSingletonMono _instance; public static PlayerSingletonMono Instance { get { if (_instance == null) { _instance = FindObjectOfType<PlayerSingletonMono>(); if (_instance == null) { _instance = new GameObject("PlayerSingletonMono").AddComponent<PlayerSingletonMono>(); DontDestroyOnLoad(_instance.gameObject); } } return _instance; } } private void OnApplicationQuit() { _instance = null; } private void OnDestroy() { _instance = null; } } ``` 这种实现方式不仅提供了线程安全的初始化过程,还能够在应用退出或对象销毁时清理资源[^4]。 --- #### 注意事项 1. 如果需要跨越多个场景,则应始终调用 `DontDestroyOnLoad(this)`。 2. 对于性能敏感的应用程序,建议减少不必要的数量,因为过多的可能导致内存泄漏或其他问题。 --- ### 总结 以上三种方法分别针对不同需求进行了优化。开发者可以根据实际项目情况选择最适合自己的实现方式。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

仙魁XAN

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值