返回

3.单例模式

同时只能存在一个对象,并提供一个快速获取这个对象的方法。

单例模式的定义

  • 同时只能存在一个对象。
  • 提供一个快速获取这个对象的方法。

单例模式在实现时,需要程序设计语言的支持。只需要具有:静态类属性、静态方法、重新定义类建造者存取层级。

Singleton参与的角色说明如下:

  • 能产生唯一对象的类,并且提供“全局方法”让外界可以方便获取唯一的对象。
  • 通常会把唯一的类对象设置为“静态类属性”。
  • 习惯上会使用 Instance 作为全局静态方法的名称,通过这个静态函数可能获取“静态类属性”。
	public class Singleton
	{
		public string Name {get; set;}

		private static Singleton _instance;
		public static Singleton Instance
		{
			get
			{
				if (_instance == null)
				{
					Debug.Log("產生Singleton");
					_instance = new Singleton();				
				}
				return _instance;
			}
		}

		private Singleton(){}
	}

少用单例模式

单例模式好用的原因是可以马上获取到类对象,不必为了安排对象传递或设置引用而伤脑筋。正因如此,单例模式很可能被滥用。

而且,单例模式还违反了“开闭原则”。因为通过Instance获取对象是“实现类”而不是接口类,该方法返回的对象包含了实现细节的实体类。因此,当设计变更或需求增加时,程序设计师无法将其替代为其他类,只能更改原有实现类内的程序代码。

如果真的需要让单例模式返回接口类——即父类为单例模式类型,让子类继承实现,并不是没有办法,有以下两种方式:

  • 子类向父类注册实体对象,让父类的Instance方法返回对象时,按条件查表返回对应的子类对象。
  • 每个子类都实现单例模式,再由父类的Instance去获取这些子类。

单例的变种

回归到单例模式的定义,需要满足两个条件:唯一、方便获取。如果我们只需要满足其中一条呢。

数量限制的单例

在由数量限制的类中加上“计数器”。每当类建造者被调用时,就让计数器加1,然后判断有没有超过限制的数量,如果超过使用上限,那么该对象就会被标记为无法使用,后续的对象功能也不可以被执行。

public class ClassWithCounter 
{
	protected static int m_ObjCounter = 0;
	protected bool m_bEnable=false;

	public ClassWithCounter()
	{
		m_ObjCounter++;
		m_bEnable = ( m_ObjCounter ==1 )? true:false ;

		if( m_bEnable==false)
			Debug.LogError("目前物件數["+m_ObjCounter+"]超過1個!!");
	}

	public void Operator()
	{
		if( m_bEnable ==false)
			return ;
		Debug.Log ("可以執行");
	}

}

设置成为类的引用

为了方便获取,我们可以在建造系统时,将单例类作为参数传入系统中,并在系统中以对象引用的形式保存起来。

public class PBaseDefenseGame
{
	public void Initinal()
	{
		// 遊戲系統
		m_GameEventSystem = new GameEventSystem(this);	// 遊戲事件系統,继承自IGameSystem
		m_CampSystem = new CampSystem(this);			// 兵營系統,继承自IGameSystem
		m_StageSystem = new StageSystem(this);			// 關卡系統,继承自IGameSystem
    }
}
public abstract class IGameSystem
{
	protected PBaseDefenseGame m_PBDGame = null;
	public IGameSystem( PBaseDefenseGame PBDGame )
	{
		m_PBDGame = PBDGame;
	}
}

指定类的静态成员

A 类的功能中若需要使用到 B 类的方法,且 A 类在产生其对象时具有下列几种情况:

  • 产生对象的位置不确定;
  • 有多个地方可以产生对象;
  • 生成的位置无法引用到;
  • 有众多子类。

当满足上述情况之一时,可以直接将B类对象设置为A类的“静态成员属性”,让该类的对象都可以直接使用;

public class PBaseDefenseGame
{
	public void Initinal()
	{
		// 遊戲系統
		m_StageSystem = new StageSystem(this);			// 關卡系統,继承自IGameSystem
    
    	// 注入到其它系統
		EnemyAI.SetStageSystem( m_StageSystem );
    }
}
public class EnemyAI : ICharacterAI 
{
	private static StageSystem	m_StageSystem = null;

	// 直接將關卡系統直接注入給EnemyAI類別使用
	public static void SetStageSystem(StageSystem StageSystem)
	{
		m_StageSystem = StageSystem;
	}
}

举例来说,敌方单位AI类(EnemyAI),在运行时需要使用关卡系统的信息,但 EnemyAI 对象产生的位置是在地方单位建造者(EnemyBuilder)之下,就可以像上面演示代码一样操作。

单例模式的应用

  • 网络在线游戏的客户端,可以使用单例来限制链接数,以预防误用而产生过多链接,避免服务器端因此失效。
  • 日志工具是比较不受项目类型影响的功能之一,所以可以设计为跨项目共享使用。此外,日志工具大多使用在调试或重要信息的输出上,而单例模式能让程序设计师方便快速地获取日志工具,所以是个不错的设计方式。