Unity 设计模式 之 创建型模式 -【单例模式】【原型模式】 【建造者模式】
目录
Unity 设计模式 之 创建型模式 -【单例模式】【原型模式】 【建造者模式】
3、 具体建造者类:木屋建造者 WoodenHouseBuilder 和砖屋建造者 BrickHouseBuilder
一、简单介绍
设计模式 是指在软件开发中为解决常见问题而总结出的一套 可复用的解决方案。这些模式是经过长期实践证明有效的 编程经验总结,并可以在不同的项目中复用。设计模式并不是代码片段,而是对常见问题的 抽象解决方案,它提供了代码结构和模块间交互的一种设计思路,帮助开发者解决特定的设计问题。
设计模式的特点:
- 通用性:设计模式针对的是软件开发中常见的设计问题,适用于各种软件工程项目。
- 可复用性:设计模式可以在不同项目和环境下被重复使用,提高代码的可维护性和扩展性。
- 可扩展性:设计模式有助于让代码结构更加灵活,易于扩展和修改。
- 模块化:通过设计模式,可以减少代码的耦合性,增强模块间的独立性。
- 提高沟通效率:设计模式为开发者提供了一种通用的设计语言,使得团队成员能够快速理解并讨论设计方案。
二、单例模式 (Singleton Pattern)
单例模式 (Singleton Pattern) 是一种创建型设计模式,保证一个类只有一个实例,并提供一个全局访问点来获取该实例。它通过控制类的实例化过程,确保系统中只有一个该类的对象存在。
在单例模式中,类的构造函数通常是私有的,防止外部通过 new
来创建对象,类内部维护一个静态实例,通过公共的静态方法提供访问。
单例模式的特点
- 唯一实例:单例模式确保一个类只有一个实例,无论系统中的组件如何调用它。
- 全局访问点:单例模式为客户端提供一个全局访问点(通常是静态方法),通过这个访问点,所有客户端都能获得相同的实例。
- 延迟初始化:可以在首次访问时创建该实例,节省系统资源(可选)。
1、什么时候使用单例模式
全局唯一的资源管理:如果一个类需要控制对某种共享资源(如数据库连接池、文件系统、日志器、配置管理器等)的访问,并且不允许有多个实例同时存在。
配置或状态共享:当系统中某个类需要持有全局配置或共享状态时,单例模式可以使得这些状态在不同模块中保持一致。
资源开销较大的对象:如果一个类的实例化代价较高(如外部资源初始化、复杂运算等),而且只需要一个实例时,单例模式可以避免多次重复的创建操作。
需要严格控制实例数量:在某些业务逻辑中,严格要求一个类只能有一个实例,否则会引发逻辑混乱或资源竞争(如操作系统中的任务管理器)。
2、单例模式的好处
控制实例数量:确保一个类只生成一个实例,避免不必要的内存占用,尤其对于管理全局状态或资源的对象(如数据库连接、日志系统、配置管理等),控制实例数量可以提高系统的稳定性。
全局访问:单例模式提供了一个全局访问点,这使得某些系统级别的服务(如日志系统、资源管理器等)能够方便地被全局访问。
节省资源:避免重复创建相同对象,减少了资源的消耗,尤其是对于耗费较大的对象(如文件系统、数据库连接等)。
线程安全:通过实现线程安全的单例,可以确保在多线程环境下,多个线程对该对象的操作是安全的。
3、使用单例模式的注意事项
-
全局状态的复杂性:虽然单例模式提供全局访问,但这也意味着它引入了全局状态,可能导致系统的状态管理变得复杂,特别是在大型系统中。
-
并发和线程安全:在多线程环境下,需要确保单例的实现是线程安全的,否则可能会出现多个线程同时创建实例的情况,导致数据不一致。
-
难以扩展:由于单例模式限制了对象的创建数量,在某些情况下,可能不利于系统的扩展或测试。比如,在单元测试中,单例模式可能难以进行模拟。
-
滥用风险:单例模式提供全局访问点,容易被滥用为全局变量,违背面向对象设计中高内聚、低耦合的原则。因此,使用时应考虑是否真的有必要将类设计为单例。
总之,单例模式 主要用于控制对象的实例化,确保系统中只有一个类的实例,并通过全局访问点来控制对象的使用。它适用于需要全局共享资源、统一管理的场景,如日志系统、数据库连接池等。尽管单例模式在某些场景下有助于提升系统的稳定性和效率,但也应谨慎使用,以避免全局状态管理复杂化或滥用全局访问带来的耦合问题。
三、在 Unity 中使用 单例模式
在 Unity 中,实现一个线程安全的普通类和MonoBehaviour 类的泛型单例时,必须考虑以下几点:
- 普通类单例:不能被
new
,并且在多线程环境下线程安全。- 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