单例模式[Singleton]
单例模式也是一种简单得不要不要的设计模式,但实用性还是很强滴。
顾名思义,单例,就是设计一种模式,让类只能有一个实例。这种需求满街可见。就看windows系统,回收站、任务管理器,就是典型的单例。用户无论怎么操作,都不可能出现两个回收站或任务管理器的。
知道其意义后,就开始设计吧,第一个模型
1 using System; 2 3 namespace Singleton 4 { 5 class Program 6 { 7 static void Main(string[] args) 8 { 9 LadyGaGa MyGirl = LadyGaGa.GetLadyGaGa(); 10 MyGirl.Dance(); 11 MyGirl.Sing(); 12 } 13 14 public class LadyGaGa 15 { 16 /// <summary> 17 /// 构造函数的访问级别被定义成了私有,这样外部就没办法直接new出来啦 18 /// </summary> 19 private LadyGaGa() { } 20 21 private static LadyGaGa ImTheOne; 22 23 /// <summary> 24 /// 这里再提供一个访问ladygaga的方法 25 /// </summary> 26 /// <returns></returns> 27 public static LadyGaGa GetLadyGaGa() 28 { 29 Console.Write("您成功邀请了Ladygaga参与你的party。当然,这个世界上如果特立独行的ladygaga只有一个"); 30 if (ImTheOne==null) 31 { 32 Console.WriteLine("哟西。ladyGaGa还没出生"); 33 ImTheOne = new LadyGaGa(); 34 } 35 return ImTheOne; 36 } 37 public void Sing() 38 { 39 Console.WriteLine("Can't read my. Can't read my. No he can't read my poker face"); 40 } 41 public void Dance() 42 { 43 Console.WriteLine("Ladgaga的舞蹈不是那么容易看到的。so hot"); 44 } 45 } 46 } 47 }
想法的核心,就是不让外部直接实例化Ladygaga。只是针对C#,要知道构造函数是可以私有的。
就到这里,已经深得单例模式的精髓,但前面的做法。是没考虑到多线程访问的时候,这个写法不是很安全就在
1 if (ImTheOne==null) 2 { 3 Console.WriteLine("哟西。ladyGaGa还没出生"); 4 ImTheOne = new LadyGaGa(); 5 }
这段代码上,如果两个线程,同一时间访问,两家判断到的结果都是null,然后两家都new一个,这时候的世界就不美妙了。因此版本二开始。
1 public static LadyGaGa GetLadyGaGa() 2 { 3 Console.Write("您成功邀请了Ladygaga参与你的party。当然,这个世界上如果特立独行的ladygaga只有一个"); 4 lock (LockerObject) 5 { 6 if (ImTheOne == null) 7 { 8 Console.WriteLine("哟西,在这个有锁的世界里,我非常确定ladyGaGa还没出生,new 一个出来吧"); 9 ImTheOne = new LadyGaGa(); 10 } 11 12 } 13 return ImTheOne; 14 }
无非就是加个锁。让这个单例做得更安全。有性能要求或处女座程序猿的情况,会想到每次访问GetLadyGaGa都要加锁,不太爽,就能再优化下。第三版
1 public static LadyGaGa GetLadyGaGa() 2 { 3 Console.Write("您成功邀请了Ladygaga参与你的party。当然,这个世界上如果特立独行的ladygaga只有一个"); 4 5 if (ImTheOne == null) 6 { 7 lock (LockerObject) 8 { 9 if (ImTheOne == null) 10 { 11 Console.WriteLine("哟西,在这个有锁的世界里,我非常确定ladyGaGa还没出生,new 一个出来吧"); 12 ImTheOne = new LadyGaGa(); 13 } 14 } 15 16 } 17 return ImTheOne; 18 }
这里的改动跟单例设计模式关系不大了,只不过想到
if (ImTheOne == null)被执行的概率很低,先不加锁判断下,非null就直接返回了嘛。
来一恶心的分介线---------------------------------------
单例模式还有什么要理解的?还真有:根据这个实例的实例发时间段,可分为饿汉,懒汉两种方式,专业的叫法是饿汉,懒汉。
饿汉:一开始就要准备好实例,其实叫贪婪方式更易懂。
懒汉:没召唤都不会有实例,对应就叫懒惰方式罗。
懒惰方式,上面coding就是一个典型的栗子,有人邀请ladgaga时,才会全世界地找ladygaga是否在人世。
贪婪方式就得好好利用static了。
1 public class LadyGaGa 2 { 3 /// <summary> 4 /// 构造函数的访问级别被定义成了私有,这样外部就没办法直接new出来啦 5 /// </summary> 6 private LadyGaGa() { } 7 8 private static LadyGaGa ImTheOne=new LadyGaGa(); 9 10 /// <summary> 11 /// 这里再提供一个访问ladygaga的方法 12 /// </summary> 13 /// <returns></returns> 14 public static LadyGaGa GetLadyGaGa() 15 { 16 return ImTheOne; 17 } 18 public void Sing() 19 { 20 Console.WriteLine("Can't read my. Can't read my. No he can't read my poker face"); 21 } 22 public void Dance() 23 { 24 Console.WriteLine("Ladgaga的舞蹈不是那么容易看到的。so hot"); 25 } 26 }
比起懒惰方式,贪婪模式写得真是简单粗暴。
个人就觉得,单例的用途上,跟一个static实例本身功能一至,但有时还真是得用单例,才能用上对象的特性,如继承。
而贪、懒方式上,要根据具体情况来选择,如对象本身实例化很漫长很庞大,应用启用又没有时效要求,就应该用贪婪方式啦。