代理模式設計

模式編程法則

1、開閉法則

開閉法則(Open Closed Principle ,OCP):軟件實體(模塊)應易於擴展(開放),但免於修改。換言之,我們希望可以改變模塊的行爲,而不用修改其源代碼。

2、Liskov 代換法則

Liskov代換法則(Liskov Substitution Principle,LSP):派生類應該可以用其基類代換。這個法則是由Barbar Liskov在研究抽象及類型理論是發現的。同時Bertrand Meyer提出的契約式設計概念中也曾提到。Liskov代換法則如下圖所示 。

Liskov

由圖可見,如果在某些User的方法中,使用類型Base(基類)作爲參數,那麼它應該允許在方法中傳入Derived(派生類)的對象實例。這就是說,Liskov代換法則利用了類型繼承關係中的向上的轉型機制。向上轉型機制是安全的,但向下轉型就不一定安全了。所以Liskov代換法則不能反過來代換,即如果一個軟件實體使用的是一個派生類作參數,那麼它不一定能適用於基類。

3、依賴反轉法則

依賴反轉法則(Dependency Inversion Principle,DIP):依賴抽象不依賴具體。

如果說OCP給出了面向對象架構的目標,那麼DIP則是指以構架的主要機制。依賴的策略強調依賴接口、抽象方法及抽象類,而不是依賴具體方法或類。

      在傳統的面向過程的設計中,人們習慣於使用高層模塊依賴於底層模塊,抽象層依賴於具體層,由上到下,形成一種,順序依賴的架構設計,這種結構從頂部主模塊開始,然後向下發展出細節部分。高層模塊依賴低層的模塊,然後再依賴低層的模塊,如此延續下去。

        這種以來結構在本質上存在着許多優點。首先作爲底層模塊其實現的具體細節,這就決定了它的多變。如果總是向下依賴於多變的下級模塊,必然會把這種變化不定的因素傳播到上層,影響系統得穩定性和健壯性。雖然通過增加中間層的諾里可以吸收底層變化因素,但中間層並不能改變這種順序依賴,相反,中間層的加入反而又導致了上層對中間層的依賴,增加了系統的複雜性。

        實際上高層模塊應該只處理應用程序的策略。這些高層策略一般很少涉及實際的細節。既然如此,爲什麼這些高層模塊必須直接依賴那些實現細節的模塊呢?如果我們改變思維,反轉依賴,得到的將是一種非常不同的依賴結構如下圖所示

這纔是一個真正的面向對象的架構,因爲高層的模塊不再依賴於含有實現細節的模塊,而是依賴抽象的接口。這種設計的好處是通過抽象接口取代被依賴的中間層,保證了上層結構的穩定。因爲底層細節實現模塊的變化並不導致接口的改變。也就是說,同一接口允許有不同的實現方法,甚至不同的實現模塊。

        依賴反轉法則的用意相當簡單。設計中的每一個依賴關係,應該是朝向接口,或一個抽象類,而不是一個具體類。這是因爲具體的東西經常改變,而抽象的東西比較穩定。抽象往往能使我們有更多的迴旋餘地。抽象層可以作爲上下層之間的樞紐,代表設計中可以伸縮或擴展的地方,而且在變動時不致於被修改,從而達到OCP的要求。打個比方,如果我們爲了記一個電話號碼而借一支筆,通常會說“借一支筆”,而不會說“借一支派克筆”或“借一支狼毫小楷毛筆”。因爲我們依賴於抽象的筆而不是具體的該筆或毛筆,這樣使得我們有迴旋的餘地,比較容易借到筆,實現記錄電話號碼的目的。相反,如果我們不依賴抽象而依賴於具體,那麼具體的筆是多變的,我們不知道對方可能有什麼樣的筆,那麼你要借一支派克筆可能會失敗,或者你不得不頻頻修改你的需求“如果沒有派克筆,,就借一支狼毫小楷毛筆”,。。。

        DIP的動機就是防止你依賴易變的具體實現。DIP假設所有具體的東西都是易變的。雖然這樣的假設有點嚴格,但在可能的情況下,這個法則應該儘可能遵循,至少組件一定要遵循這個法則。如COM就堅持這個法則。COM組件唯一可以看到的部分就是其抽象的接口。因此在COM中很少逾越DIP

        DIP也是模式編成的法則。例如,在設計中最長髮生依賴的地方式建構對象。依據定義,你無法建構抽象類的對象實例。因此,爲了構造一個對象實例,你必須依賴一個具體類。在架構設計中的鵝任何地方都可能發生構造對象實例的情況。因此,似乎無法逃脫這種狀況,而整個構架會被這種依賴具體類的事實弄亂。但是抽象工廠模式提供了一個簡單的解決方法。下面討論了代理模式如何實現DIP,從而消除業務層對數據層的依賴,因爲業務層依賴於數據層的具體API會限制業務層的擴展和充用。

        在多層系統的設計中,通過代理模式引入的代理層,可以使得原來相互依賴的層之間實現依賴反轉,從而滿足層與層之間獨立演化的需求。特別在業務層依賴數據層或其他第三方中間層時,這種設計可以很好的消除耦合。

4、藉口隔離法則

接口隔離法則(Interface Segregation PrincipleISP):不應該強迫客戶端依賴於它們用不上的方法。

接口隔離的法則就是說,因不同的客戶端而劃分出多個不同的專用接口,比使用單一的總的接口好。若果從客戶類的角度看,一個類對另一個類的依賴應該是建立在最小的接口上的。換言之,我們編成時應當把那些客戶端不必要的接口從使用接口的客戶端隔離開來。

ISP是模式編成的法則之一。沒這個法則,組件及類將缺乏效用和可移植性。

這個法則的實質相當簡單。如果你的淚有許多客戶端調用,你不必向客戶端開放所需的所有方法,而只需要爲每一個客戶端建立特定的接口,然後在類中多重繼承這些接口。

5、單一職責法則

單一職責法則(Single-Responsibility PrincipleSRP):一個類應該僅有一個原因導致其變化。

單一職責法則是指一個類應該只有一個演化方向,那門這個類的變化纔是可控的。這裏的所謂職責指“一個導致變化的原因”。因爲調整一個類所承擔的職責纔是我們改動這個類的真正動機,所以導致變化的原因源於類的職責。如果一個類包含過多的職責,這就意味着會有多個導致其變化的原因。而且,這些變化往往不是孤立的,他們通過多個職責間的耦合關係相互作用,是類產生多個相互依賴的不確定的演化方向,帶來維護和重用上的困難。

一個類應該僅有一個原因導致其變化,意味着它們的函數關係是一元的:

C = f(x)

這裏的x就是導致變化的原因 — 職責。

但是在大量的不良設計中,卻使一個類中存在多個致變的因素,這正是由於一個類中承擔了太多的職責,於是它們的函數關係呈現多元的。當一個類中包含多個職責,且他們的耦合關係錯綜複雜時,如果分離不當,會造成循環依賴的問題。對這種職責不易分離的情況,可以採用前面的接口隔離法則。

. 代理模式

1. 概述

       所謂代理模式(Proxy)就是爲某一個對象的訪問提供一個代理對象,而不是直接去控制對象。這個對象在客戶端和源對象之間起着中介作用。

       例如,有客戶來買某種Computer,但是這種Computer僅有少數幾家供貨商提供。客戶很難找到這些供貨商。即使找到了,也可能遇到這些供貨商缺貨的麻煩。如果客戶通過代理商來購買這種Computer,事情都變得很簡單了,因爲代理商知道那些供貨商有貨源。於是代理商在客戶對象和Computer對象之間起到了中介的作用。

 

 

1是產品代理模式的示意圖。客戶對象(TClient)通過代理購買Computer的,他所關心的不再是具體產品及其生產和供貨的途徑,而是抽象的產品,既作爲接口的產品說明說。供貨商是產品說明書的直接實現者,他嚴格按照產品說明書的要生產和提供符合要求的產品,代理商雖然也按照產品說明書的要求代理產品,但他的責任是根據產品說明書的要求選擇合格的供貨商來實現提供的產品,也就是說他自己並不具體實現該產品,他只是一箇中介。由於有了代理商,因而客戶不必知道那些供貨商生產和提供產品,供貨商也不必知道那些客戶需要產品。也就是說自從有了代理商,客戶和供貨商之間的依賴關係不符存在,代理商也起到了解耦的作用。

在代理模式中,其核心包含接口、代理和實現三部分。接口抽象了客戶感興趣的操作,以方便客戶訪問對象。代理和實現都是對接口的實現,但其實現方式不同。一個接口可能會有多種不同的實現;而代理可以通過選擇和使用有效的具體實現來間接實現接口,爲客戶提供服務。所以說,代理並不僅僅是多了一道手續而是一項增值服務,代理通過自身的業務邏輯可以幫助具體實現解決問題、克服不做,提升服務質量。

       在軟件開發中,經常使用代理來解決對象與對象、系統與系統之間的強耦合,起到中介與緩衝的作用。使用代理模式可以控制存取一個對象,特別是那些耗費資源的對象,代理模式可以將對象延遲到真正需要的時在創建。

       代理是一個經常用到的概念,而且種類繁多。可以使用代理模式的常見模式有;

     遠程代理(Remote Proxy)爲一個對象在不同的地址空間提供局部代表對象。這個不同的地址空間可以是在本機,也可以是在其他機器中。

     虛代理(Virtual Proxy)根據需要創建開銷很大的對象,使得該對象僅僅在實際使用時纔會被真正創建。

     保護代理(Protection Proxy)控制原始的對象的訪問。保護代理可以提供或限制用戶訪問用戶的不同權限。

     智能引用(Smart Proxy)當一個對象被引用時,只能引用取代了簡單的指針。它提供了一些訪問對象時的附加操作,比如檢查管理引用計數,當引用爲零時,負責自動釋放對象;檢查引用對象是否鎖定,確保不盲目改變對象的狀態;檢查並確保持久化對象是在首次加載時裝入內存的。

2 結構和用法

2.1 模式結構

3

代理模式的結構如圖3所示,它包括了一下參與者:

     代理(TProxy)— 負責維護一個引用,使得代理可以訪問真實的主題對象;提供一個與主題一直的藉口讓代理可以替代真實的對象;控制對真實對象的訪問,並可能負責創建和刪除它;把客戶端的調用傳遞給真實的主題之前或之後,執行某些輔助性的操作。

     抽象主題(TSubject)— 定義TRealSubject TProxy 的共同接口;這樣就在任何使用 TRealSubject 的地方都可以用TPtoxy

     真實主題(TRealSubjec)— 定義代理對象可以代理的真正對象。

4

4 是在執行期間的代理模式時序圖。由圖可見代理模式工作的過程爲,客戶端Client向代理對象Proxy發出請求,代理對象依據代理的種類適時傳遞請求給真實的對象RealSubjec。這樣,客戶端對象、代理對象、真實主題對象之間建立了如圖 5 所示的關係。

5

由此可見,與客戶端直接向真實對象發出請求的情況相比,代理可以通過間接傳遞請求而提供比較靈活的中介控制。比如,代理可以在傳遞請求的前後執行一些附加操作,完成引用計數、保護檢查等操作;代理可以決定何時、何地及如何創建或銷燬所代理的真實對象。總之,代理模式將一個過渡曾插入到客戶端和真實主題之間,提供了一種遊刃有餘的編程藝術。

對於客戶而言,原先要調用的真實對象可以被代理對象置換。因爲他們實現的都是TSubjec 的共同接口,所以在客戶端看來沒什麼差別。這就是說,使用代理模式解決了問題,但絲毫不影響到客戶端。

2.2 給予代理模式的優化設計

       在設計時,我們都知道要把用戶界面(User InterfaceUI)、業務邏輯()和數據()分層,程序員的水平高低決定了對這種分層的理解和實現。分層不僅僅是把實現邏輯代碼分開,更重要的是合理邏輯設計它們的依賴關係,減低和簡化它們的彼此間的耦合關係。

       例如在圖6所示的分層架構設計中,由於業務層和數據層關係糾纏不清,造成循環依賴;表現層同時依賴於業務層和數據層,造成依賴關係複雜。這樣雖然貌似分層,但實際上併爲降低耦合度,達不到分層的真正效果。

                                                                                                

                     6                                                                     7

       7 是單一依賴關係得分層,即表現層依賴於業務層,業務層依賴於數據層。這種分層的架構設計是我們經常使用的,它改進了圖6所示的混亂的依賴關係,使得層與層之間的關係清晰、簡單和易於維護。

       7所示的層次關係中,表現層提供的圖形界面,讓用戶完成業務操作,所以表現層依賴於業務層是合理的。但是業務層對數據層的過分依賴並不是一件好事。因爲我們可能會有不同的數據存儲及管理需求以及基於不同平臺的不同實現,比如,數據層可能使用 RDBMSXML 等不同的持久機制,可能使用ODBCADOBDEJDBC等不同的數據訪問接口(API)。爲了適應這種變化,提高系統的健壯性,我們可以在業務層和數據層間加一層代理層,以吸收業務層和數據層的變化因素,消除它們之間的耦合,如圖 8 所示

8

       注意圖8所示的代理層分別依賴業務層和數據層,箭頭的方向不同於圖7那樣的順序。這就是說代理層依賴於業務層,而不是業務層依賴於代理層。如果是按圖7那樣的依賴順序加入代理層,即業務層依賴代理層,代理層依賴數據層,那麼數據層的變化仍然有可能通過依賴它的代理層傳播到業務層,通過依賴反轉法則(Dependency Inversion Principle)徹底隔絕了數據層對業務層的影響。

       9是基於代理模式對分層的設計。根據依賴反轉法則“依賴抽象而不依賴具體”的思想,我們可以把代理模式的抽象主題(Subject)作爲抽象接口讓表現層和代理層依賴,而在真實的主題(RealSubject)中實現業務邏輯,並在代理(Proxy)中完成業務層和數據層的膠合。這樣一來,代理層就爲業務層和數據層之間的Mapping提供了集中統一的實現。

發佈了33 篇原創文章 · 獲贊 0 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章