通俗道破---單例模式


    很多時候我們不知不覺中使用着設計模式,自己很多卻不知道自己使用了,例如我們在涉及抽象類,接口的時候經常用到裝飾者模式,在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 }

運行效果如下:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM