天天看點

系統架構技能之設計模式-單件模式

       其實我本來不是打算把系統架構中的一些設計模式單獨抽出來講解的,因為很多的好朋友也比較關注這方面的内容,是以我想通過我了解及平時項目中應用到的一

些常見的設計模式,拿出來給大家做個簡單講解,我這裡隻是抛磚引玉,如果某個地方講解的不正确或者不詳細,請大家批評指出。園子裡面的很多的大牛寫的設計模式

都非常的經典,我這裡寫可能有點班門弄斧的感覺,不過我還是決定把它寫出來,希望能對初學者有一定的幫助和指導的作用。當然我這裡如果說某個地方解釋的有問

題或者說是某個地方寫的不符合邏輯之處,還請大家多多指出,提出寶貴意見。

       軟體工程中其實有很多總結性的話語,比如說軟體=算法+資料結構等等這樣的描述,當然我們這裡可能算法就是泛指一些軟體中的程式設計方法了,設計模式怎麼去

了解呢?為什麼要有設計模式?它能帶來什麼?等等這些都是我們需要讨論的問題。首先我們需要知道設計模式能帶來什麼。可能這才是我們學習它的主要原因,如果

說不能為我們在書寫軟體的過程中帶來更方面的好處,那我們也不會使用和學習它。

       設計模式是什麼?

       設計模式可以簡單的了解為解決某一系列問題的完美的解決方案。我們在軟體開發的過程中經常遇到設計功能實作的問題,而設計模式正是為了解決軟體設計功能

實作時遇到的某一類問題的解決方案。因為一般情況下來說,我們在某個軟體功能的開發過程中遇到的功能設計問題,可能是前人很早就遇到過的問題,是以通過這種

設計模式的方式來解決,能讓我們在軟體實作的過程中少走彎路,或者說是給我們的軟體設計帶來很好的靈活性和适應性。

       設計模式帶來了什麼?

       設計模式是源于實踐,并且每種設計模式都包含了一個問題描述,問題涉及到的參與者并且提供了一個實際的解決方案。設計模式的好處我們可以通過下圖來簡單

說明:

計模式帶來了這麼多的好處,是以我們學習設計模式就顯得比較必要了,也是從事軟體開發及設計必須掌握的基本技能之一。

       設計模式的簡單分類:

式開始講解,我想建立型模式也是大家項目中必備的吧?下面我就從建立型模式先來講解。

       本文将主要講解建立型模式中的單例模式先來講解,因為單例模式是最簡單也是最容易了解的設計模式,上手快,易使用的設計模式。本文将從下面的流程來講解

單例模式,後面講述的設計模式也将使用這樣的方式。

       1、什麼是單例模式?

       2、單例模式的應用場景。

       3、舉例說明單例模式的使用。

       4、總結單例模式的用法。

       a、開篇。

       b、摘要。

       c、本文大綱。

       d、單例模式的簡介。

       e、相關應用場景分析。

       f、本文總結。

       g、系列進度。

       h、下篇預告。

       本章我們将來講述下單例模式的使用,首先我們來看看單例模式的定義:

       單例模式:是一種軟體設計中常用的設計模式,主要是用來控制某個類必須在某個應用程式中隻能有一個執行個體存在。

       有時候我們需要確定整個系統中隻有某個類的一個執行個體存在,這樣有利于我們協調控制系統的行為。例如:我們在某個系統中使用了發送短信的這樣的服務,那麼

我們可能希望通過單一的短信服務類的執行個體,而不是多個對象執行個體完成短信的發送服務。這時我們可以通過單例模式來完成。

        我們看看單例模式的幾種實作方式:

        下面我們來舉例說明下這2種方式的實作。

        1、外部控制的方式

<a href="http://blog.51cto.com/2435232/603401#">?</a>

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

<code>public</code> <code>class</code> <code>Instance</code>

<code>{</code>

<code>    </code><code>private</code> <code>List&lt;SendMessage&gt; lists = </code><code>new</code> <code>List&lt;SendMessage&gt;();</code>

<code>    </code><code>private</code> <code>SendMessage sendInstance;</code>

<code>    </code><code>public</code> <code>SendMessage SInstance</code>

<code>    </code><code>{</code>

<code>        </code><code>get</code>

<code>        </code><code>{</code>

<code>            </code><code>return</code> <code>sendInstance;</code>

<code>        </code><code>}</code>

<code>    </code><code>}</code>

<code>    </code><code>public</code> <code>void</code> <code>InstanceMethod()</code>

<code>        </code><code>if</code> <code>(lists.Count == 0)</code>

<code>            </code><code>sendInstance = </code><code>new</code> <code>SendMessage();</code>

<code>            </code><code>lists.Add(sendInstance);</code>

<code>        </code><code>else</code>

<code>            </code><code>sendInstance = lists[0];</code>

<code>}</code>

       2、内部控制方式

<code>public</code> <code>class</code> <code>Instance1</code>

<code> </code><code>{</code>

<code>    </code><code>private</code> <code>static</code> <code>SendMessage sendInstance;</code>

<code>    </code><code>private</code> <code>static</code> <code>object</code> <code>_lock = </code><code>new</code> <code>object</code><code>();</code>

<code>    </code><code>protected</code> <code>Instance1()</code>

<code>    </code><code>public</code> <code>static</code> <code>SendMessage SInstance</code>

<code>            </code><code>lock</code> <code>(_lock)</code>

<code>            </code><code>{</code>

<code>                </code><code>if</code> <code>(sendInstance == </code><code>null</code><code>)</code>

<code>                    </code><code>sendInstance = </code><code>new</code> <code>SendMessage();</code>

<code>                </code><code>return</code> <code>sendInstance;</code>

<code>            </code><code>}</code>

        這裡有幾點需要注意的地方,對于第二種方式有幾個地方需要說明下,首先是要控制全局隻有一個執行個體的類,請定義成靜态執行個體,這樣可以確定隻有一個執行個體對

象,其次,這個對象的構造函數請聲明成保護類型的成員,這樣可以屏蔽通過直接執行個體化的形式來通路。通過這樣的形式,客戶可以不需要知道某個單例執行個體對象的内

部實作細節。一般情況下滿足上面的2點需求就可以完成全局唯一通路入口的控制。當然可能在多線程的情況下采用這樣的形式還會有一定的弊端,當然我們這裡也簡單

的講解下相應的控制方案。方案如下:

<code>public</code> <code>class</code> <code>CoolInstance</code>

<code>    </code><code>private</code> <code>CoolInstance()</code>

<code>    </code><code>public</code> <code>static</code> <code>readonly</code> <code>CoolInstance Instance = </code><code>new</code> <code>CoolInstance();</code>

       看吧很簡單吧,當然我們這裡來簡單解釋下原理:

       1、我們先把構造函數聲明為私有的構造函數,這樣我們能夠屏蔽外部通過執行個體化的形式通路内部的成員函數。所有的成員函數的通路必須通過靜态成員Instance

來完成通路。

       2、這段代碼通過定義公共、靜态、隻讀的成員相當于在類被第一次使用時執行構造,由于是隻讀的,是以一旦構造後不允許修改,就不用擔心不安全的問題。

        相信對上面的介紹大家應該基本上知道單例模式的應用了,那麼下面我們來看看項目中的實際應用場景及用法。

        1、場景短信及郵件發送服務

        那麼我們将采用上面介紹的最“COOL”的方式來進行控制,提供發送短信及發送郵件的服務。

<code>    </code><code>/// &lt;summary&gt;</code>

<code>    </code><code>/// 發送手機短信</code>

<code>    </code><code>/// &lt;/summary&gt;</code>

<code>    </code><code>public</code> <code>bool</code> <code>SendMessage(</code><code>string</code> <code>telNumber,</code><code>string</code> <code>content)</code>

<code>        </code><code>return</code> <code>true</code><code>;</code>

<code>    </code><code>/// 發送郵件</code>

<code>    </code><code>/// &lt;param name="content"&gt;&lt;/param&gt;</code>

<code>    </code><code>/// &lt;param name="toMail"&gt;&lt;/param&gt;</code>

<code>    </code><code>public</code> <code>bool</code> <code>SendMail(</code><code>string</code> <code>content,</code><code>string</code> <code>toMail)</code>

        我們再來看看調用類中如何書寫完成調用。例如我們有個訂單類,當有人新下訂單時,将給賣家發送短信提醒功能。

27

28

29

30

31

32

33

34

35

36

37

<code>/// &lt;summary&gt;</code>

<code> </code><code>/// 訂單業務</code>

<code>/// &lt;/summary&gt;</code>

<code>public</code> <code>class</code> <code>Order</code>

<code>    </code><code>public</code> <code>int</code> <code>Save()</code>

<code>        </code><code>//先是将訂單的相關資訊生成,</code>

<code>        </code><code>this</code><code>.InitOrderInfo();</code>

<code>        </code><code>//執行訂單的持久化方法</code>

<code>        </code><code>int</code> <code>count= </code><code>this</code><code>.Add();</code>

<code>        </code><code>//發送短信</code>

<code>        </code><code>CoolInstance.Instance.SendMessage(</code><code>string</code><code>.Empty, </code><code>string</code><code>.Empty);</code>

<code>        </code><code>//發送郵件</code>

<code>        </code><code>CoolInstance.Instance.SendMail(</code><code>string</code><code>.Empty, </code><code>string</code><code>.Empty);</code>

<code>        </code><code>return</code> <code>count;</code>

<code>    </code><code>/// 初始化訂單資訊</code>

<code>    </code><code>private</code> <code>void</code> <code>InitOrderInfo()</code>

<code>    </code><code>/// 新增訂單資訊</code>

<code>    </code><code>/// &lt;returns&gt;&lt;/returns&gt;</code>

<code>    </code><code>private</code> <code>int</code> <code>Add()</code>

<code>        </code><code>return</code> <code>0;</code>

        這樣我們就完成了短信發送服務及郵件發送服務的控制。主要還是根據自己的業務需要。

        2、例如我們現在提供一個系統日志服務或者列印或者掃描的服務,我們希望全局隻有一個通路入口,那麼我們就可以通過這樣的單例模式來實作這樣的需求。

<code>public</code> <code>class</code> <code>PrintHelper</code>

<code>     </code><code>#region 構造函數</code>

<code>     </code><code>private</code> <code>PrintHelper()</code>

<code>     </code><code>{</code>

<code>     </code><code>}</code>

<code>     </code><code>public</code> <code>static</code> <code>readonly</code> <code>PrintHelper Instance = </code><code>new</code> <code>PrintHelper();</code>

<code>     </code><code>#endregion</code>

<code>     </code><code>#region 列印服務</code>

<code>     </code><code>/// &lt;summary&gt;</code>

<code>     </code><code>/// 直接列印服務</code>

<code>     </code><code>/// &lt;/summary&gt;</code>

<code>     </code><code>/// &lt;returns&gt;&lt;/returns&gt;</code>

<code>     </code><code>public</code> <code>bool</code> <code>Print()</code>

<code>         </code><code>return</code> <code>true</code><code>;</code>

<code>     </code><code>/// 列印預覽</code>

<code>     </code><code>public</code> <code>bool</code> <code>PrintPreview()</code>

<code> </code><code>}</code>

        具體的調用類我就不寫相應的代碼,都和上面的形式類同,下面我們講解下可能更特殊的需求,有時候我們可能需要更新我們建立的唯一執行個體,這時我們如何控

制單例執行個體對象的更新呢,有時候可能我們有這樣的需求。下面我們來看看如何實作這樣的需求。

        3、可更新單例對象的場景

        首先我們先說下什麼情況下會遇到這樣的更新方式呢?例如我們想在單例模式的類的構造函數是帶有一定參數的情形時:

<code>public</code> <code>class</code> <code>UpdateHelper</code>

<code>    </code><code>private</code> <code>string</code> <code>type = </code><code>string</code><code>.Empty;</code>

<code>    </code><code>private</code> <code>static</code> <code>UpdateHelper instance;</code>

<code>    </code><code>private</code> <code>UpdateHelper(</code><code>string</code> <code>valueType)</code>

<code>        </code><code>type = valueType;</code>

<code>    </code><code>public</code> <code>static</code> <code>UpdateHelper Instance</code>

<code>                </code><code>if</code> <code>(instance == </code><code>null</code><code>)</code>

<code>                </code><code>{</code>

<code>                    </code><code>//如果這裡有多個條件需求的話,可能寫起來會比較複雜,那麼有更好的方式來處理嗎?</code>

<code>                    </code><code>instance = </code><code>new</code> <code>UpdateHelper(</code><code>"test!"</code><code>);</code>

<code>                </code><code>}</code>

<code>                </code><code>return</code> <code>instance;</code>

        那麼我們來分析幾種辦法,有沒有更好的辦法來處理呢?

        1、首先我們不能手動執行個體化,是以我們沒有辦法動态傳入構造函數參數,隻能在類的内部指定這個參數,但是有時候我們需要動态的更新這個參數,那麼這樣的

形式顯然就沒有辦法實作。

        2、通過屬性的方式,來動态的設定屬性的内容來完成輸出參數的改變,但是這樣的方式可能太過自由,無法滿足單例模式的初衷。

        3、接口方式,因為接口必須要靠類來實作,是以更不靠譜,可以不考慮這樣的方式。

        4、通過Attribute的方式來将資訊動态的注入到構造函數中,但是怎麼說這樣的方式是不是太興師動衆了呢?畢竟單例模式本來就是很簡單的。

        5、通過配置檔案,通過config檔案配置節點的形式來動态的配置相關資訊,實作更新執行個體對象内容的情況。

        通過上面的5種情況的分析,那麼通過2、4、5可以實作這個要求,但是對比相應的代價來說,5的方式是最靈活也是最符合單例模式本來的規範要求,相對來說

成本和代價也可以接收。

<code>&lt;?</code><code>xml</code> <code>version</code><code>=</code><code>"1.0"</code> <code>encoding</code><code>=</code><code>"utf-8"</code> <code>?&gt;</code>

<code>&lt;</code><code>configuration</code><code>&gt;</code>

<code>  </code><code>&lt;</code><code>system.Web</code><code>&gt;</code>

<code>    </code><code>&lt;</code><code>add</code> <code>key</code><code>=</code><code>"ssss"</code> <code>&gt;value&lt;/</code><code>add</code><code>&gt;</code>

<code>  </code><code>&lt;/</code><code>system.Web</code><code>&gt;</code>

<code>&lt;/</code><code>configuration</code><code>&gt;</code>

         那麼我們上面的單力模型中的代碼隻需要稍微的變化下即可,請看如下代碼:

<code>                    </code><code>instance = </code><code>new</code> <code>UpdateHelper(System.Configuration.ConfigurationManager.AppSettings[</code><code>"ssss"</code><code>].ToString());</code>

        我想到這裡大家都對單例模式有個簡單的認識了,本文的内容就講到這裡。我們來回顧下我們講述的内容:

        本文主要講述了建立型模式中的單例模式,單例模式主要是用來控制系統中的某個類的執行個體的數量及全局的通路入口點。我們主要講述了實作單例模式的方式,

分為外部方式及内部方式,當然我們現在采用的方式都是内部方式,還講述了線程安全的單例模式及帶有參數的構造函數的情況,根據配置檔案來實作參數值的動态配

置的情況。希望本文的講解能對不熟悉設計模式的同仁能夠了解知道單例模式的應用,而對已熟知單例模式的同仁可以溫故而知新,我會努力寫好這個系列,當然我這

裡可能在大牛的面前可能是班門弄斧吧,不過我會繼續努力,争取寫出讓大家一看就明白的設計模式系列。本文錯誤之處再所難免,還請大家批評之處,我會繼續改

進。

        下篇我們将會介紹我們大家最熟知的工程模式,當然我會更多的結合執行個體來講解每個設計模式的應用場景及具體的執行個體,來更清晰的描述什麼情況下用什麼模

式,及每個模式之間的差別。大家的支援就是我書寫的動力,希望大家多多支援我吧!

本文轉自 hot的fans  51CTO部落格,原文連結:http://blog.51cto.com/2435232/603401

繼續閱讀