1. 意图
保证一个类仅有一个实例,并提供一个访问它的全局访问点
2. 适用性
在下面的情况下可以使用Singleton模式
当类只能有一个实例而且客户可以从一个众所周知的访问点访问它是。
当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时。
讨论
1. 为什么是Singleton模式而不是用静态方法?
考虑以下代码
public static class Singleton { public static void MethodA() { } }
这样的代码大部分时候可以解决问题,并不是说不能这么做,准确的说法是Singleton模式比静态方法更加灵活(请见下文)
2. 最简单的Singleton模式的实现
public class Singleton { private static Singleton _instance = null; protected Singleton() { } public static Singleton Instance() { if (_instance == null) _instance = new Singleton(); return _instance; } }
通常这样已经够用了,如果严格的来看这段代码还是有一些小问题
1) 多线程访问时不一定能保证单例
2) 虽然通过把构造函数标记为 protected来避免外部创建Singleton对象,但是我们依然可以通过反射来避开它。只能说反射太强大了...
3. 解决多线程问题的Singleton
public class Singleton { private static Singleton _instance = null; private static readonly object obj = new object(); protected Singleton() { } public static Singleton Instance() { if (_instance == null) { lock (obj) { if (_instance == null) _instance = new Singleton(); } } return _instance; } }
4. .NET中可以利用语言特性来实现Singleton模式
利用静态构造函数
public class Singleton { private static readonly Singleton _instance = new Singleton(); static Singleton() { } protected Singleton() { } public static Singleton Instance() { return _instance; } }
具体可以参考 http://msdn.microsoft.com/zh-cn/library/k9x6w0hc(v=VS.100).aspx
注意,这种实现不是多线程安全的,如果需要多线程安全请参考3
以上只是Singleton模式的基本应用,很多文章里都有类似的例子,其实Singleton模式还有很多特性经常被大家忽略
5. 允许可变数目的实例
这不是扩展,GoF明确指出了Singleton模式允许可变数目的实例,GoF没有给出明确的实现,不过实现也很简单。
这种情况用到的不多。
6.单例模式的通用抽象类
书中并没有提出这个概念,不过我们可以很容易的定义一个抽象的单例父类,让子类达到“单例”的效果
要注意的是子类必须将构造函数保护起来,仅此而已
namespace Singleton1_3 { class Program { static void Main(string[] args) { MySingletonA a = Singleton.Instance<MySingletonA>(); MySingletonA aa = MySingletonA.Instance<MySingletonA>(); if (a == aa) Console.WriteLine("aaa"); } } abstract class Singleton { private static Dictionary<Type, Singleton> list = new Dictionary<Type, Singleton>(); protected Singleton() { } public static T Instance<T>() where T : Singleton { T _instance = default(T); Type t = typeof(T); foreach (KeyValuePair<Type, Singleton> item in list) { if (item.Key.Name.Equals(t.Name, StringComparison.CurrentCultureIgnoreCase)) { _instance = item.Value as T; break; } } if (_instance == null) { System.Runtime.Remoting.ObjectHandle objectHandle = Activator.CreateInstance(Assembly.GetExecutingAssembly().FullName, t.FullName, true, BindingFlags.Default | BindingFlags.Instance | BindingFlags.NonPublic, null, null, null, null); _instance = objectHandle.Unwrap() as T; list.Add(t, _instance); } return _instance; } } class MySingletonA : Singleton { private MySingletonA() { } } class MySingletonB : Singleton { private MySingletonB() { } } }
7. 单件注册表(Registry of Singleton)
首先区分一个概念, 这里讲的是Registry of Singleton, 重点是通过Registry来实现Singleton,
Martin Fowler在企业应用架构模式中提出Registry模式通常会用单例实现,说的是Registry模式的实现,这两个是不相同的。
单件注册表可以理解为Singleton有多个子类,我们需要在运行时决定使用具体的某个Singleton子类的实例,可以简单的理解为Singleton多个子类是排他性的
这种场景在实际开发中用的不多,这里不做讨论
Singleton模式介绍完毕,接下来我们考虑Singleton和静态方法的区别
1. 静态方法关注的是方法,操作 例如Convert.ToInt32() 某些时候没有必要去写类似于 new Converter().Convert(xxx ...) 的代码
2. Singleton模式的主角是一个class, 在OO中代表了一个对象,例如总统,既然是一个class 它就可以继承自某个基类(例如President是一个单例,它继承自Person
你让President 变成静态类提供静态方法,多么奇怪?
3. 这个class还可以实现某个接口,静态方法就不行。在一个多层系统中通常层与层之间会提供一系列的接口,一个实现了这个接口的Singleton class显然比需要知道具体类的静态方法来的灵活很多