通俗道破---单例模式
很多时候我们不知不觉中使用着设计模式,自己很多却不知道自己使用了,例如我们在涉及抽象类,接口的时候经常用到装饰者模式,在Winfrom的窗体(当然还是类)复用中经常用到模板方法模式......反正什么是设计模式,学好多态是很重要的,言归正传。
通常一个类通常可以创建无限个对象,但是有时候需要只有一个对象的类,比如全局资源管理器、缓存管理器等,这种情况下如果有多个对象就会乱掉了。缓存管理器只能有一个,否则把数据扔给一个管理器,却管另外一个要。
就好比有一个老婆类,他的一个对象对我很好,但是不是随便那个老婆等可以对我好,应该只有我老婆对我好,因此我们要确定这个唯一的老婆。
单例模式的意思就是只有一个实例。单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。这个类称为单例类。
现在我们来看一个不是单例模式却有点类似的味道的小代码(至于为什么我稍后解释):
(读取文件内容:)未优化前的代码:
1 static void Main(string[] args) 2 { 3 Stopwatch sw=new Stopwatch(); 4 sw.Start(); 5 for (int i = 0; i < 100000; i++) 6 { 7 string s=File.ReadAllText(@"d:\1.txt"); 8 Console.WriteLine(s); 9 } 10 sw.Stop(); 11 Console.WriteLine("耗时:"+sw.Elapsed); 12 Console.ReadKey(); 13 }
优化以后:
1 static void Main(string[] args) 2 { 3 Stopwatch sw=new Stopwatch(); 4 sw.Start(); 5 string s = null; 6 for (int i = 0; i < 100000; i++) 7 { 8 if(s==null) 9 { 10 s = File.ReadAllText(@"d:\1.txt"); 11 } 12 Console.WriteLine(s); 13 } 14 sw.Stop(); 15 Console.WriteLine("耗时:"+sw.Elapsed); 16 Console.ReadKey(); 17 }
我先解释为什么不是单例模式,我们应该知道string 是CLR中String类的别名,也就是说我可以在这个小程序中创建很多的String类的对象,而且可以是不同的(string a="";string b="123"......),所以没有实现确保某一个类只有一个实例的要求。
我们来看一下优化前后代码执行的不同点,是第二段代码在每次循环的时候先查看下s是不是为空,如果不为空直接打印出s,不需要再次读文件(当然此处的判断也是耗时间的哦,好像昨天一个网友还刚发表了什么关于不能从时间的长度看效率,反正我没看,不过这里咱们知道有优化效果就行了,要是真想提高效率必须研究数据结构和算法)此处的s起了一个缓存的作用,使执行时间大大缩短。
单例模式的应用:
有时候我们会遇到这种需求,就是频繁的从服务器下载资料进行对比,或者频繁的操作文件,我想了下vss的工作机制应该也是这个道理,如果我们的开发团队没有修改代码,调用的时候还是会返回缓存中的代码给某个程序员。
譬如每台计算机可以有若干个打印机,但只能有一个Printer Spooler, 以避免两个打印作业同时输出到打印机中。每台计算机可以有若干传真卡,但是只应该有一个软件负责管理传真卡,以避免出现两份传真作业同时传到传真卡中的情况。每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。
等等情况... ...
怎么实现单例模式
实现单例模式的方法很多,常用的就像百度百科里面提到的:
第一种形式: 也是常用的形式。
1 public class Singleton 2 { 3 private static Singleton instance = null; 4 private Singleton() 5 { //do something 6 } 7 public static Singleton getInstance() 8 { 9 if (instance == null) 10 { instance = new Singleton(); } 11 return instance; 12 } 13 } //这个方法比下面的有所改进,不用每次都进行生成对象,只是第一次使用时生成实例,提高了效率
但是上面这个百度百科没有说明,就是如果是多线程并行操作可能会引起实例的冲突。
所以最好这样啦:
public static Singleton getInstance()
{
return instance;
}
第二种形式:
1 public class Singleton 2 { //在自己内部定义自己的一个实例,只供内部调用 3 private static Singleton instance = new Singleton(); 4 private Singleton() 5 { 6 //do something 7 } 8 //这里提供了一个供外部访问本class的静态方法,可以直接访问 9 public static Singleton getInstance() 10 { 11 return instance; 12 } 13 }
现在我们模拟频繁读取同一个文件的内容(使用单例模式):
1 using System; 2 using System.IO; 3 using System.Diagnostics; 4 using System.Collections.Generic; 5 6 namespace 单例模式 7 { 8 class Program 9 { 10 static void Main(string[] args) 11 { 12 Stopwatch sw = new Stopwatch(); 13 sw.Start(); 14 for (int i = 0; i < 10000; i++) 15 { 16 //因为Instance是唯一的实例 17 string s= FileCache.instance.ReadFile(@"d:\1.txt"); 18 Console.WriteLine(s); 19 } 20 sw.Stop(); 21 Console.WriteLine(sw.Elapsed); 22 Console.ReadKey(); 23 } 24 } 25 class FileCache 26 { 27 public static readonly FileCache instance = new FileCache(); 28 private FileCache() 29 { 30 } 31 //创建一个字典用来保存文件信息,key是文件名,value是文件内容 32 private Dictionary<string, string> dic = new Dictionary<string, string>(); 33 public string ReadFile(string path) 34 { 35 if (dic.ContainsKey(path)) 36 { 37 return dic[path]; 38 } 39 string content = File.ReadAllText(path); 40 dic.Add(path, content); 41 return content; 42 } 43 } 44 }
上面的代码我的硬件配置是i52430,2G CUP 运行了不到一秒。这段代码确保了我们每次操作的都是同一个实例。但是还有个问题就是我们我们及时在程序执行期间修改了文本文件里的内容,显示的仍然没有更新,怎么动态的修改文件并更新显示呢,这里我们就要新建一个内容管理类,保存上次更新的时间和现在内容,就可以了
1 using System; 2 using System.IO; 3 using System.Diagnostics; 4 using System.Collections.Generic; 5 using System.Threading; 6 7 namespace 单例模式 8 { 9 class Program 10 { 11 static void Main(string[] args) 12 { 13 Stopwatch sw = new Stopwatch(); 14 sw.Start(); 15 for (int i = 0; i < 100000000; i++) 16 { 17 //因为Instance是唯一的实例 18 string s= FileCache.instance.ReadFile(@"d:\1.txt"); 19 Thread.Sleep(500); 20 Console.WriteLine(s); 21 } 22 sw.Stop(); 23 Console.WriteLine(sw.Elapsed); 24 Console.ReadKey(); 25 } 26 } 27 class FileCache 28 { 29 public static readonly FileCache instance = new FileCache(); 30 private FileCache() 31 { 32 } 33 private Dictionary<string, FileContentManager> dic = new Dictionary<string, FileContentManager>(); 34 public string ReadFile(string path) 35 { 36 if (dic.ContainsKey(path)) 37 { 38 DateTime lastwritetime = File.GetLastWriteTime(path);//获取上次写入文件的时间 39 if (lastwritetime == dic[path].LastWriteTime) 40 { 41 return dic[path].Content; 42 } 43 } 44 string content = File.ReadAllText(path); 45 FileContentManager txtmanger = new FileContentManager(); 46 ///========将信息保存到文件管理类中========== 47 txtmanger.Content = content; 48 txtmanger.LastWriteTime = File.GetLastWriteTime(path); 49 ///========将信息保存到文件管理类中========== 50 return content; 51 } 52 } 53 class FileContentManager 54 { 55 public string Content { get;set;}//内容 56 public DateTime LastWriteTime { get; set; }//修改时间 57 } 58 }
运行效果如下: