轉自:http://www.cnblogs.com/sevenyuan/archive/2010/03/05/1678730.htmlhtml
及:http://blog.csdn.net/lovelionjava
及:http://blog.sohu.com/s/Mzc2Nzc5MjU/239020230.htmlmysql
在OO分析與設計中,咱們首先從問題領域中抽象出領域模型,在領域模型中以適當的粒度概括出相關的類;而後定義各個類之間的關聯關係,並給這些類分配相應的職責,同時定義這些類之間的協做方式。將相應的職責分配給具體的類是OO過程當中很是重要的一步。GRASP設計模式是職責分配過程當中的一套很是重要的設計模式。它給出了在給類分配職責的過程當中,設計者們所須要聽從的一些原則或者指導性的建議。sql
說到設計模式,更爲人所知的固然是GoF(Gang of Four)的23種設計模式。與GoF的23種設計模式不一樣的是,GRASP設計模式描述的是在OO設計中爲互相協做的類分配職責的原則或者建議,而GoF的設計模式則是在更高的層次上描述一個OO系統或者其局部系統的行爲以及結構上的抽象。數據庫
要學習設計模式,有些基礎知識是咱們必需要先知道的,設計模式是關於類和對象的一種高效、靈活的使用方式,也就是說,必須先有類和對象,纔能有設計模式的用武之地,不然一切都是空談,那麼類和對象是從那冒出來的呢?這時就須要比23種設計模式更重要更經典的GRASP模式登場了,嘿嘿,原來這纔是老大!編程
GRASP(General Responsibility Assignment Software Patterns),中文名稱爲「通用職責分配軟件模式」,它由《UML和模式應用》(Applying UML and Patterns)一書做者Craig Larman提出。與其將它們稱之爲設計模式,不如稱之爲設計原則,由於它是站在面向對象設計的角度,告訴咱們怎樣設計問題空間中的類與分配它們的行爲職責,以及明確類之間的相互關係等,而不像GoF模式同樣是針對特定問題而提出的解決方案。所以GRASP站在一個更高的角度來看待面向對象軟件的設計,它是GoF設計模式的基礎。GRASP是對象職責分配的基本原則,其核心思想是職責分配(Responsibility Assignment),用職責設計對象(Designing Objects with Responsibilities)。GRASP一共包括9種模式,它們描述了對象設計和職責分配的基本原則。也就是說,如何把現實世界的業務功能抽象成對象,如何決定一個系統有多少對象,每一個對象都包括什麼職責,GRASP模式給出了最基本的指導原則。初學者應該儘快掌握、理解這些原則,由於這是如何設計一個面向對象系統的基礎。能夠說,GRASP是學習使用設計模式的基礎。設計模式
1. Information Expert Pattern(信息專家模式)安全
信息專家模式是面向對象設計的最基本原則,是咱們平時使用最多,應該跟咱們的思想融爲一體的原則。也就是說,咱們設計對象(類)的時候,若是某個類擁有完成某個職責所須要的全部信息,那麼這個職責就應該分配給這個類來實現。這時,這個類就是相對於這個職責的信息專家。服務器
可參考以下理解:網絡
(1) 問題:給對象分配職責的通用原則是什麼?
(2) 解決方案:將職責分配給擁有履行一個職責所必需信息的類,即信息專家。
(3) 分析:信息專家模式是面向對象設計的最基本原則。通俗點來說,就是一個類只幹該乾的事情,不應乾的事情不幹。在系統設計時,須要將職責分配給具備實現這個職責所須要信息的類。信息專家模式對應於面向對象設計原則中的單一職責原則。
信息專家模式關注這樣一個問題:給對象分配職責的基本原則是什麼?
在一個OO系統中可能會定義成百上千個軟件類,而全部這些類必須履行的職責的數量之和甚至更多。若是能很好地給全部這些類分配好職責,那麼這個系統就會易於理解、維護和擴展。信息專家模式關於這個問題給出的答案是:把職責分配給信息專家,它具備實現這個職責所必需的信息。
這一設計模式理解起來很是簡單,它並非某種深度認證的結論,而更像是一種直覺,即對象完成與它所具備的信息相關的那些職責。有許多例子都能說明這一點,其中最多見的包括各類GUI庫的繪圖函數。好比iOS的UIKit框架中的layoutSubviews方法、wxWidgets庫中的OnPaint方法、MFC框架中的OnPaint方法、Swing庫中的paint/updata/repaint方法,等等。這些方法都是將GUI界面的繪製功能分配到了每個具體的視圖類中,不管是庫/框架中已經存在的具體視圖,或者是用戶自定義的具體視圖。由於只有這些具體的視圖類才擁有繪製自身所必需的信息,它們能很好地履行繪製自身的這一職責。這是信息專家模式的一個直接應用。
在信息專家模式給出的建議中,因爲對象能夠使用自身的信息來完成它的職責,所以信息的封裝性得以維護,從而支持了低耦合;同時,因爲類的職責都根據自身所擁有的信息來分配,於是該模式也支持高內聚的設計。
固然,在某些特殊的狀況下,信息專家模式也許並不適用,這一般是因爲內聚與耦合的問題所產生的。好比許多後臺系統都有把模型(Model)類的數據存入數據庫的功能。這一職責的履行所必需的信息顯然是存在於各個模型類中,按照信息專家模式給出的建議,應該讓這些模型類來完成把自身的數據保存到數據庫中的功能。可是,這樣的設計會致使內聚與耦合方面的問題。首先,在這樣的設計中,全部的模型類都必須包含與數據庫處理相關的邏輯,如與JDBC相關的處理邏輯。這使得模型類因爲其餘職責的存在而下降了它的內聚。其次,這樣的設計也爲全部的模型類都引入了與JDBC的耦合關係,使得系統的耦合度上升。甚至,這種設計也會致使大量重複、冗餘的數據庫邏輯存在於整個系統中的各個角落,這也違反了設計要分離主要的系統關注的基本架構原則。所以,在這種狀況下,信息專家模式須要咱們結合整個系統的耦合和內聚作出另外的考慮。
例如:常見的網上商店裏的購物車(ShopCar),須要讓每種商品(SKU)只在購物車內出現一次,購買相同商品,只須要更新商品的數量便可。(SKUID)來惟一區分商品,而商品編號是惟一存在於商品類裏的,因此根據信息專家模式,應該把比較商品是否相同的方法放在商品類裏。
2. Creator Pattern(創造者模式)
實際應用中,符合下列任一條件的時候,都應該由類A來建立類B,這時A是B的建立者:
a. A是B的聚合
b. A是B的容器
c. A持有初始化B的信息(數據)
d. A記錄B的實例
e. A頻繁使用B
若是一個類建立了另外一個類,那麼這兩個類之間就有了耦合,也能夠說產生了依賴關係。依賴或耦合自己是沒有錯誤的,可是它們帶來的問題就是在之後的維護中會產生連鎖反應,而必要的耦合是逃不掉的,咱們能作的就是正確地建立耦合關係,不要隨便創建類之間的依賴關係,那麼該如何去作呢?就是要遵照建立者模式規定的基本原則,凡是不符合以上條件的狀況,都不能隨便用A建立B。
可參考如下的理解:
(1) 問題:誰應該負責產生類的實例?
(2) 解決方案:若是符合下面的一個或者多個條件,則可將建立類A實例的職責分配給類B:
B包含A;
B聚合A;
B擁有初始化A的數據並在建立類A的實例時將數據傳遞給類A;
B記錄A的實例;
B頻繁使用A。
此時,咱們稱類B是類A對象的建立者。若是符合多個條件,類B聚合或者包含類A的條件優先。
(3) 分析:建立對象是面向對象系統中最廣泛的活動之一,所以,肯定一個分配建立對象的通用職責很是重要。若是職責分配合理,設計就能下降耦合,提升設計的清晰度、封裝性和重用性。一般狀況下,若是對象的建立過程不是很複雜,則根據上述原則,由使用對象的類來建立對象。可是若是建立過程很是複雜,並且可能須要重複使用對象實例或者須要從外部注入一個對象實例,此時,能夠委託一個專門的工廠類來輔助建立對象。建立者模式與各類工廠模式(簡單工廠模式、工廠方法模式和抽象工廠模式)相對應。
建立者模式關注這樣一個問題:假設系統中存在一個類A,那麼在這個系統中,誰應該負責建立類A的新實例?
建立類的實例是面向對象的系統中最多見的活動之一。合理分配類的建立職責的設計可以支持低耦合,提升封裝性、可複用性、可擴展性。建立者模式爲這一活動給出的指導性的建議是,將建立類A的新實例的職責分配給類B,若是如下條件成立:
l B「包含」或組成彙集A。
l B記錄A。
l B直接使用A。
l B具備A的初始化數據,而且在建立A的實例時會將這些數據傳遞給A。
這些條件成立得越多越好。若是有一個以上的條件成立,那麼一般首選彙集或包含A的類B。
舉一個很是簡單的例子。假設系統中存在鏈表類和節點類。一個鏈表類的對象包含了多個節點的對象,鏈表類的用戶(這裏的用戶不是指現實世界中的人,而是指使用鏈表類的代碼)能夠向一個鏈表類的對象插入一個新的節點。那麼,被插入的新的節點對象應該由誰來建立?顯然,鏈表類的用戶自己是一個建立者的候選者,由於用戶自己擁有節點對象的初始化數據,由用戶建立節點對象是能夠實現的。然而,根據建立者模式,鏈表類倒是一個更好的選擇。由於鏈表類包含了節點,並直接使用、操做節點。由鏈表類自己建立節點能夠消除鏈表類的用戶對於節點類的依賴關係,從而消除了用戶代碼與節點類的耦合,使系統中僅剩下用戶代碼與鏈表類的耦合。這造成了一個良好的設計。
使用建立者模式的好處是不會增長系統的耦合度,由於根據建立者模式的建議,類的實例的建立者已經與這個類存在着某種形式的耦合。所以該模式支持低耦合的設計,能產生具備較低的維護依賴性與較高的複用性的系統。
在一個設計靈活的OO系統中,對象的建立方式每每很是複雜。好比,有些系統須要爲了更好的性能而集中建立或者使用回收的實例(線程池、鏈接池、對象池等);有些系統須要根據某些條件來建立一族類的實例;甚至有些框架系統在框架的編寫過程當中根本不知道須要實例化哪個類,等等。在這些狀況下,最好的方法是將建立類的實例的職責委派給抽象工廠(Abstract Factory)、具體工廠(ConcreteFactory)、建立器(Builder)等等輔助類,而不是建立者模式所建議的類。
例如:由於訂單(Order)是商品(SKU)的容器,因此應該由訂單來建立商品。
3. Low coupling Pattern(低耦合模式)
低耦合模式的意思就是要咱們儘量地減小類之間的鏈接。
其做用很是重要:
a. 低耦合下降了因一個類的變化而影響其餘類的範圍。
b. 低耦合使類更容易理解,由於類會變得簡單,更內聚。
下面這些狀況會形成類A、B之間的耦合:
a. A是B的屬性
b. A調用B的實例的方法
c. A的方法中引用了B,例如B是A方法的返回值或參數。
d. A是B的子類,或者A實現了B
關於低耦合,還有下面一些基本原則:
a. Don’t Talk to Strangers原則:
意思就是說,不須要通訊的兩個對象之間,不要進行無謂的鏈接,鏈接了就有可能產生問題,不鏈接就一了百了啦!
b. 若是A已經和B有鏈接,若是分配A的職責給B不合適的話(違反信息專家模式),那麼就把B的職責分配給A。
c. 兩個不一樣模塊的內部類之間不能直接鏈接,不然必招報應!嘿!
可參考以下理解:
(1) 問題:怎樣支持低的依賴性,減小變動帶來的影響,提升重用性?
(2) 解決方案:分配一個職責,使得保持低耦合度。
(3) 分析:耦合是評價一個系統中各個元素之間鏈接或依賴強弱關係的尺度,具備低耦合的元素不過多依賴其餘元素。此處的元素能夠是類,也能夠是模塊、子系統或者系統。具備高耦合的類過多地依賴其餘類,這種設計將會致使:一個類的修改致使其餘類產生較大影響;系統難以維護和理解;系統重用性差,在重用一個高耦合的類時不得不重用它所依賴的其餘類。所以須要對高耦合的系統進行重構。
類A和類B之間的耦合關係體現以下:A具備一個B類型的屬性;A調用B的方法;A的方法包含對B的引用,如方法參數類型爲B或返回類型爲B;A是B的直接或者間接子類;B是一個接口,A實現了該接口。低耦合模式鼓勵在進行職責分配時不增長耦合性,從而避免高耦合可能產生的不良後果。在進行類設計時,須要保持類的獨立性,減小類變動所帶來的影響,它一般與信息專家模式和高內聚模式一塊兒出現。爲了達到低耦合,咱們能夠經過以下方式對設計進行改進:
在類的劃分上,應當儘可能建立鬆耦合的類,類之間的耦合度越低,就越有利於複用,一個處在鬆耦合中的類一旦被修改,不會對關聯的類形成太大波及;
在類的設計上,每個類都應當儘可能下降其成員變量和成員函數的訪問權限;
在類的設計上,只要有可能,一個類型應當設計成不變類;
在對其餘類的引用上,一個對象對其餘對象的引用應當降到最低。
低耦合模式關注這樣一個問題:怎樣下降依賴性,減小變化帶來的影響,提升重用性?
低耦合模式關於這個問題給出的答案是:分配職責,使耦合度儘量低。
耦合是系統設計中最重要的概念之一,也是設計中真正的基本原則之一。所謂耦合,指的是對某元素與其餘元素之間的鏈接、感知和依賴程度的度量。在一個OO系統中,全部的耦合形式可分爲5類:
l 零耦合(nil coupling):兩個類絲絕不依賴於對方。
l 導出耦合(export coupling):一個類依賴於另外一個類的公有接口。
l 受權耦合(overt coupling):一個類經容許,使用另外一個類的實現細節。
l 自行耦合(covert coupling):一個類未經容許,使用另外一個類的實現細節。
l 暗中耦合(surreptitious coupling):一個類經過某種方式知道了另外一個類的實現細節。
零耦合固然是耦合度最低的。兩個絲毫互不依賴的類,意味着在維護和擴展系統時,能夠隨意地去掉或者修改其中的一個類而絲絕不會影響到另外一個類。可是,只使用零耦合卻沒法建立出一個有意義的OO系統,由於全部的類都是獨立、不相關的,相互之間沒有消息的傳遞,這樣最多隻能建立出一個類庫。導出耦合具備至關低的耦合度,由於在導出耦合中,一個類只依賴另外一個類的公有接口。在一個設計良好的系統中,消息的傳遞只會經過類的公有接口進行,於是導出耦合能夠很好地支持系統的可維護性與可擴展性。除此以外,受權耦合、自行耦合、以及暗中耦合都是耦合程度比較高的耦合形式。
有這樣一條OO設計的經驗原則:類與類之間應該零耦合,或者只有導出耦合關係。也即,一個類要麼同另外一個類毫無關係,要麼就只使用另外一個類提供的公有接口。受權耦合、自行耦合、暗中耦合基本上不該該在系統中被使用到。
類A到類B或接口B的常見的導出耦合形式有:
l A具備引用B的實例或B自身的屬性。
l A的實例調用B的實例的服務。
l A具備以任何形式引用B的實例或B自身的方法。
l A是B的直接或間接子類。
l B是接口,而A是此接口的實現。
低耦合模式提倡職責分配要避免產生具備負面影響的高耦合。低耦合模式支持在設計時下降類的依賴性,減小變化所帶來的影響。好比在一般狀況下,系統每每能以使用關係來替換繼承關係,由於繼承關係是一種耦合程度很是高的強耦合形式,而使用關係能下降耦合度。
固然,沒有絕對的度量標準來衡量耦合程度的高低。使用低耦合模式的目的是爲了建立一個可靈活伸縮的、可維護的、可擴展的系統。在這個目的之下,低耦合不能脫離信息專家和高內聚等其餘模式孤立地考慮,而是應該同時權衡耦合與內聚。高耦合自己也並非問題之所在,問題是與某些方面不穩定的元素之間的高耦合,這種高耦合會嚴重影響系統未來的維護性和擴展性。而好比全部的Java系統都能安全地將本身去Java庫(java.lang,java.util等)進行耦合,由於Java庫是穩定的,與Java庫的耦合不會給系統的靈活性、維護性、擴展性帶來什麼問題。
例如:Creator模式的例子裏,實際業務中須要另外一個出貨人來清點訂單(Order)上的商品(SKU),並計算出商品的總價,可是因爲訂單和商品之間的耦合已經存在了,那麼把這個職責分配給訂單更合適,這樣能夠下降耦合,以便下降系統的複雜性。TotalPrice()方法來執行計算總價的職責,沒有增長沒必要要的耦合。
4. High cohesion Pattern(高內聚模式)
高內聚的意思是給類儘可能分配內聚的職責,也能夠說成是功能性內聚的職責。即功能性緊密相關的職責應該放在一個類裏,並共同完成有限的功能,那麼就是高內聚合。這樣更有利於類的理解和重用,也便於類的維護。
高內聚也能夠說是一種隔離,就想人體由不少獨立的細胞組成,大廈由不少磚頭、鋼筋、混凝土組成,每個部分(類)都有本身獨立的職責和特性,每個部份內部發生了問題,也不會影響其餘部分,由於高內聚的對象之間是隔離開的。
可參考以下理解:
(1) 問題:怎樣使得複雜性可管理?
(2) 解決方案:分配一個職責,使得保持高內聚。
(3) 分析:內聚是評價一個元素的職責被關聯和關注強弱的尺度。若是一個元素具備不少緊密相關的職責,並且只完成有限的功能,則這個元素就具備高內聚性。此處的元素能夠是類,也能夠是模塊、子系統或者系統。
在一個低內聚的類中會執行不少互不相關的操做,這將致使系統難於理解、難於重用、難於維護、過於脆弱,容易受到變化帶來的影響。所以咱們須要控制類的粒度,在分配類的職責時使其內聚保持爲最高,提升類的重用性,控制類設計的複雜程度。爲了達到低內聚,咱們須要對類進行分解,使得分解出來的類具備獨立的職責,知足單一職責原則。在一個類中只保留一組相關的屬性和方法,將一些須要在多個類中重用的屬性和方法或完成其餘功能所需的屬性和方法封裝在其餘類中。類只處理與之相關的功能,它將與其餘類協做完成複雜的任務。
高內聚模式關注這樣一個問題:怎樣保持對象是有重點的、可理解的、可管理的,而且可以支持低耦合?
高內聚模式關於這個問題給出的答案是:分配職責,使其可保持較高的內聚性。
同耦合同樣,內聚也是系統設計中最重要的概念之一,也是設計中真正的基本原則之一。所謂內聚(內聚有多種類型,包括偶然內聚、邏輯內聚、時間內聚、通訊內聚、順序內聚、功能內聚、信息內聚等,這裏主要指的是功能內聚),是對元素(包括類、子系統等)職責的相關性和集中度的度量。若是元素具備高度相關的職責,並且沒有過多工做,那麼該元素具備高內聚性。
高內聚的類的方法數量較少,在功能性上有很是強的關聯,並且不須要作太多的工做。若是任務的規模比較大的話,應該將任務所涉及到的各項職責按照關聯的強度分配到各個類中,而後讓各個類的對象進行相互協做,共同完成這項任務。高內聚的類優點明顯,它易於理解、維護和複用。高度相關的功能性與少許操做相結合,也能夠簡化維護和改進的工做。細粒度的、高度相關的功能性也能夠提升複用的潛力。
以信息專家模式一節中最後一段中所舉的例子來講,若是按照信息專家給出的建議,把與數據庫進行交互的職責都放進模型類中,那麼這樣的模型類顯然作了過多的工做。這是一種很差的低內聚設計。從數據的角度來說,內聚程度低的模型類因爲加入了數據庫操做職責,它須要在一個對象中同時維護兩大塊數據,一塊是模型自己的數據,另外一塊則是數據庫操做須要用到的數據。用於進行數據庫操做的數據顯然會在各個模型類的對象中造成冗餘。從功能的角度來說,數據庫操做功能的加入直接致使了模型類中包含了兩種大相徑庭的功能和邏輯,這使得模型類更加難以理解、複用和維護。這樣的設計是十分脆弱的,會常常容易受到變化的影響。而一個高內聚的設計則是將數據庫相關邏輯從模型類中移除,放入專門的對象或者子系統中。這樣,模型類自身便能擁有更高的內聚度,它們所包含的功能容易被人理解;同時,在面向接口的良好設計中,模型類也更加容易被維護以及擴展。
在實踐中,高內聚模式也不能脫離其餘模式(如信息專家和低耦合)單獨地考慮。高內聚與低耦合是在整個系統的設計過程當中,須要不斷地去考慮和評估的基本原則。
在少數狀況下,較低的內聚也是被接受的。好比,爲了方便專門的數據庫邏輯人員統一管理SQL語句,系統中每每能夠將與SQL語句相關的操做都放在一個獨立的全能類中;另外,出於性能的考慮,在RPC中使用一個粗粒度的RPC服務器類,既能夠減小服務器上對象的數目,能夠減小網絡請求和鏈接的數目,從而提升系統的性能。這些,都須要從系統的全局出發,結合多種設計的原則進行權衡考慮。
例如:一個訂單數據存取類(OrderDAO),訂單便可以保存爲Excel模式,也能夠保存到數據庫中;那麼,不一樣的職責最好由不一樣的類來實現,這樣纔是高內聚的設計,Excel的功能發生錯誤,那麼就去檢查OrderDAOExcel類就能夠了,這樣也使系統更模塊化,方便劃分任務,好比這兩個類就能夠分配個不一樣的人同時進行開發,這樣也提升了團隊協做和開發進度。
5. Controller Pattern(控制器模式)
用來接收和處理系統事件的職責,通常應該分配給一個可以表明整個系統的類,這樣的類一般被命名爲「XX處理器」、「XX協調器」或者「XX會話」。
關於控制器類,有以下原則:
a. 系統事件的接收與處理一般由一個高級類來代替。
b. 一個子系統會有不少控制器類,分別處理不一樣的事務。
關於這個模式更詳細的論述,請參考《UML和模式應用》第16章。
可參考以下理解:
(1) 問題:誰應該負責處理一個輸入系統事件?
(2) 解決方案:把接收或者處理系統事件消息的職責分配給一個類。這個類能夠表明:
整個系統、設備或者子系統;
系統事件發生時對應的用例場景,在相同的用例場景中使用相同的控制器來處理全部的系統事件。
(3) 分析:一個控制器是負責接收或者處理系統事件的非圖形用戶界面對象。一個控制器定義一組系統操做方法。在控制器模式中,要求系統事件的接收與處理一般由一個高級類來代替;一個子系統須要定義多個控制器,分別對應不一樣的事務處理。一般,一個控制器應當把要完成的功能委託給其餘對象,它只負責協調和控制,自己不完成太多的功能。它能夠將用戶界面所提交的請求轉發給其餘類來處理,控制器能夠重用,且不能包含太多業務邏輯,一個系統一般也不能設計一個統一的控制器。控制器模式與MVC模式相對應,MVC是一種比設計模式更加高級的架構模式。
控制器模式關注這樣一個問題:在UI層之上首先接收和協調(控制)系統操做的第一個對象是什麼?
系統操做就是系統中的主要輸入事件,好比按鈕的點擊、文字的輸入等。控制器模式對於這個問題的回答是:使用控制器做爲UI層之上的第一個對象,它負責接收和處理系統操做消息。而控制器是能表明如下選擇之一的類:
l 表明整個「系統」、「根對象」、運行軟件的設備或主要子系統,這些是外觀控制器的變體。
l 表明用例場景,在該場景中發生系統事件,一般命名爲<UseCaseName>Handler、<UseCaseName>Coordinator或<UseCaseName>Session。
在系統中,諸如「窗口」(Windows)、「視圖」(View)或「文檔」(Document)之類的類主要負責顯示的功能,並不屬於控制器。這些類不該該完成與系統事件相關的任務。一般狀況下,它們接收這些事件,並將其委派給控制器,即委派模式。
例如,在iPhone應用程序的開發框架UIKit中,許多繼承自UIView類的具體視圖類都具備一個叫作delegate的屬性。其中包括UIScrollView類,它表示一個滾動視圖,添加到其中的子視圖的尺寸能夠超過滾動視圖自身的尺寸,並在滾動視圖中進行滾動顯示。而全部UIScrollView類的實例均可以指定一個委派delegate,其類型是UIScrollViewDelegate(接口)。用戶能夠自行實現UIScrollViewDelegate這個接口,定義好處理各種事件的方法,好比滾動開始事件、滾動結束事件等等。當UIScrollView類的實例接收到系統操做以後,該實例會將這些操做委派給用戶指定的delegate進行處理,即該delegate中相應的處理方法會獲得調用。
控制器設計中的常見缺陷是分配的職責過多。這時,控制器會具備很差的低內聚。於是存在這樣一條準則:正常狀況下,控制器應當把須要完成的工做委派給其餘的對象。控制器只是協調或控制這些活動,自己並不完成大量工做。
6. Polymorphism (多態)
這裏的多態跟OO三大基本特徵之一的「多態」是一個意思。
可參考以下理解:
(1) 問題:如何處理基於類型的不一樣選擇?如何建立可嵌入的軟件組件?
(2) 解決方案:當相關選擇或行爲隨類型(類)變化而變化時,用多態操做爲行爲變化的類型分配職責。
(3) 分析:由條件變化引起同一類型的不一樣行爲是程序的一個基本主題。若是用if-else或switch-case等條件語句來設計程序,當系統發生變化時必須修改程序的業務邏輯,這將致使很難方便地擴展有新變化的程序。另外對於服務器/客戶端結構中的可視化組件,有時候須要在不影響客戶端的前提下,將服務器的一個組件替換成另外一個組件。此時能夠使用多態來實現,將不一樣的行爲指定給不一樣的子類,多態是設計系統如何處理類似變化的基本方法,基於多態分配職責的設計能夠方便地處理新的變化。在使用多態模式進行設計時,若是須要對父類的行爲進行修改,能夠經過其子類來實現,不一樣子類能夠提供不一樣的實現方式,將具體的職責分配給指定的子類。新的子類增長到系統中也不會對其餘類有任何影響,多態是面向對象的三大基本特性之一(另外兩個分別是封裝和繼承),經過引入多態,子類對象能夠覆蓋父類對象的行爲,更好地適應變化,使變化點可以「經得起將來驗證」。多態模式在多個GoF設計模式中都有所體現,如適配器模式、命令模式、組合模式、觀察者模式、策略模式等等。
多態性模式關注這樣一個問題:如何處理基於類型的選擇?如何建立可插拔的軟件構件?
關於這個問題,多態性模式給出的答案是:當相關選擇或行爲隨類型(類)有所不一樣時,使用多態操做爲變化的行爲類型分配職責。不要測試對象的類型,也不要使用條件邏輯來執行基於類型的不一樣選擇。
例如,在一個電子商務的系統中,有一個Order類用來表示用戶提交的一份訂單,其中包含了訂單所須要的數據,如商品ID列表、每種商品對應的數量等等;以及相關的一些行爲,如計算商品的總價格等等。根據信息專家模式給出的建議,計算該訂單給消費者產生了多少消費稅的這一功能,也應該由Order類來完成(假設方法名叫calculateTax)。然而,根據消費者所在國家的不一樣,消費稅的計算方式明顯是不同的。有一種解決方案是,將用戶所在國家的名稱以字符串或枚舉的形式,連同消費總值一塊兒,做爲參數傳遞給calculateTax方法。而後在calculateTax方法中,經過一連串的if else語句來判斷參數傳入的是哪一個國家,並給出相應的計算代碼。
這種方法的確能在某種程度上解決問題。可是這種方案極不利於系統的維護和擴展。首先,設想一下該商務系統支持100多個國家的業務,那麼程序中的100多個elseif語句會是怎樣一種壯觀的景象。這是極不利於系統的維護者去閱讀甚至查找某一個國家對應的elseif語句。其次,一旦未來中止了某些國家的業務,或者須要增長新國家的業務,或者須要修改已有的一些國家的稅率,除了須要在一大堆的elseif中作一些很繁瑣的工做以外,Order這個類自己的代碼是須要被從新編譯才能繼續使用的。這無疑給維護和擴展帶來了麻煩。
問題的另外一種解決方案則是利用多態性模式。系統首先定義出一個接口,好比叫作TaxCalculator。該接口定義了惟一一個方法,calculateTax,該方法根據消費總值計算消費稅。而後,對於每個國家的不一樣的消費稅計算方法,系統再分別定義專門的類去實現這個接口。好比,計算英國消費者的消費稅的類爲UKTaxCalculator,計算美國消費者的消費稅的類爲USTaxCalculator,等等。全部這些類都根據自身所表明的國家的具體消費稅計算方法來實現了calculateTax方法。也即,前一種解決方案中的100多個elseif語句中的內容被分拆到了100多個類中。如今,當Order類須要計算消費稅的時候,傳遞給它的calculateTax方法的參數就只須要一個消費總值和一個TaxCalculator的引用了,而且它自己不須要作任何具體的計算,它只須要將這一操做下派給這個引用就能夠了。
上面的這個例子其實就是GoF設計模式中的策略模式,這的確是多態性模式的一個經典的應用場景。經過多態性模式,系統分離了接口與具體的實現,從而在保持接口不變的狀況下,能夠很是容易地對實現進行維護和擴展。而且,在上面的例子中,對具體策略類的各類修改都不須要在對Order類進行重編譯。
多態性模式是OO設計的一個基本原則。
例如:咱們想設計一個繪圖程序,要支持能夠畫不一樣類型的圖形,咱們定義一個抽象類Shape,矩形(Rectangle)、圓形(Round)分別繼承這個抽象類,並重寫(override)Shape類裏的Draw()方法,這樣咱們就能夠使用一樣的接口(Shape抽象類)繪製出不一樣的圖形,(Diamond)類,對整個系統結構也沒有任何影響,只要增長一個繼承Shape的類就好了。
7. Pure Fabrication (純虛構)
這裏的純虛構跟咱們常說的純虛構函數意思相近。高內聚低耦合,是系統設計的終極目標,可是內聚和耦合永遠都是矛盾對立的。高內聚覺得這拆分出更多數量的類,可是對象之間須要協做來完成任務,這又形成了高耦合,反過來毅然。該如何解決這個矛盾呢,這個時候就須要純虛構模式,由一個純虛構的類來協調內聚和耦合,能夠在必定程度上解決上述問題。
可參考以下理解:
(1) 問題:當不想破壞高內聚和低耦合的設計原則時,誰來負責處理這種狀況?
(2) 解決方案:將一組高內聚的職責分配給一個虛構的或處理方便的「行爲」類,它並非問題域中的概念,而是虛構的事務,以達到支持高內聚、低耦合和重用的目的。
(3) 分析:純虛構模式用於解決高內聚和低耦合之間的矛盾,它要求將一部分類的職責轉移到純虛構類中,在理想狀況下,分配給這種虛構類的職責是爲了達到高內聚和低耦合的目的。在實際操做過程當中,純虛構有不少種實現方式,例如將數據庫操做的方法從數據庫實體類中剝離出來,造成專門的數據訪問類,經過對類的分解來實現類的重用,新增長的數據訪問類對應於數據持久化存儲,它不是問題域中的概念,而是軟件開發者爲了處理方便而產生的虛構概念。純虛構能夠消除因爲信息專家模式帶來的低內聚和高耦合的壞設計,獲得一個具備更好重用性的設計。在系統中引入抽象類或接口來提升系統的擴展性也能夠認爲是純虛構模式的一種應用。純虛構模式一般基於相關功能的劃分,是一種以功能爲中心的對象或行爲對象。在不少設計模式中都體現了純虛構模式,例如適配器模式、策略模式等等。
純虛構模式關注這樣一個問題:當你並不想違背高內聚和低耦合或其餘目標,可是基於專家模式所提供的方案又不合適時,哪些對象應該承擔這一職責?
OO設計中的領域模型是對領域內的概念內或現實世界中的對象的模型化表示。建立領域模型的關鍵思想是減少軟件人員的思惟與軟件模式之間的表示差別。所以,在OO設計時,系統內的大多數類都是來源於現實世界中的真實類。然而,在給這些類分配職責時,有可能會遇到一些很難知足低耦合高內聚的設計原則。純虛構模式對這一問題給出的方案是:給人爲製造的類分配一組高內聚的職責,該類並不表明問題領域的概念,而表明虛構出來的事物,用以支持高內聚、低耦合和複用。
純虛構模式強調的是職責應該置於何處。通常來講,純虛構模式會經過表示解析或者行爲解析來肯定出一些純虛構類,用於放置某一類職責。理想情況下,分配給這種虛構物的職責是要支持高內聚低耦合的,從而使整個系統處於一種良好的設計之中。
例如,在信息專家模式的最後一段所舉的例子中提到,許多後臺系統都須要對數據庫進行操做,將系統中的一些對象進行持久化。信息專家模式給出的建議是將持久化的職責分配給具體的每個模型類。可是這種建議已經被證實是不符合高內聚低耦合原則的,由於不會被採納。因而,設計者每每會在系統中加入相似於DAO或者PersistentStorage這樣的類。這些類在領域模型中是並不存在的,它們徹底由設計者根據系統的行爲而虛構獲得。然而,這些類的引入,使得操做數據庫進行持久化這種高內聚的職責能夠瓜熟蒂落地分配給它們。從而在整個系統中實現了比較好的內聚和耦合。
在使用純虛構模式時,不能毫無限制地對系統中的各類行爲進行解析並純虛構。如此每每會致使系統中大量的行爲對象的存在,這樣會對耦合產生不良的影響。
例如:上面多態模式的例子,若是咱們的繪圖程序須要支持不一樣的系統,那麼由於不一樣系統的API結構不一樣,繪圖功能也須要不一樣的實現方式,那麼該如何設計更合適呢?AbstractShape,不管是哪一個系統均可以經過AbstractShape類來繪製圖形,咱們即沒有下降原來的內聚性,也沒有增長過多的耦合,可謂魚肉和熊掌兼得,哈哈哈!
8. Indirection (間接性)
「間接」顧名思義,就是這個事不能直接來辦,須要繞個彎才行。繞個彎的好處就是,原本直接會鏈接在一塊兒的對象彼此隔離開了,一個的變更不會影響另外一個。就想我在前面的低耦合模式裏說的同樣,「兩個不一樣模塊的內部類之間不能直接鏈接」,可是咱們能夠經過中間類來間接鏈接兩個不一樣的模塊,這樣對於這兩個模塊來講,他們之間仍然是沒有耦合/依賴關係的。
可參考以下理解:
(1) 問題:如何分配職責以免兩個(或多個)事物之間的直接耦合?如何解耦對象以下降耦合度並提升系統的重用性?
(2) 解決方案:分配職責給中間對象以協調組件或服務之間的操做,使得它們不直接耦合。中間對象就是在其餘組件之間創建的中介。
(3) 分析:要避免對象之間的直接耦合,最經常使用的作法是在對象之間引入一箇中間對象或中介對象,經過中介對象來間接相連。中介模式對應於面向對象設計原則中的迪米特法則,在外觀模式、代理模式、中介者模式等設計模式中都體現了中介模式。
間接性模式關注這樣一個問題:爲了不兩個或多個事務之間直接耦合,應該如何分配職責?如何使對象解耦合,以支持低耦合並提升複用性潛力?
間接性模式對此的回答是:將職責分配給中介對象,使其做爲其餘構件或服務之間的媒介,以免它們之間的直接耦合。中介則實現了其餘構件之間的間接性。
間接性模式的思想比較簡單,即經過一箇中介就能消除許多的耦合。在GoF的23種設計模式中,有許多模式都利用到了間接性的思想。好比橋接模式中,設計將抽象部分與其實現部分相分離,利用的就是在客戶與實現之間增長了一個抽象層次。外觀模式則是在整個子系統與客戶之間增長了一個便於用戶使用的外觀類做爲中介。而中介者模式中的中介者則更是典型的例子。
9. Protected Variations (受保護變化,防止變異)
預先找出不穩定的變化點,使用統一的接口封裝起來,若是將來發生變化的時候,能夠經過接口擴展新的功能,而不須要去修改原來舊的實現。也能夠把這個模式理解爲OCP(開閉原則)原則,就是說一個軟件實體應當對擴展開發,對修改關閉。在設計一個模塊的時候,要保證這個模塊能夠在不須要被修改的前提下能夠獲得擴展。這樣作的好處就是經過擴展給系統提供了新的職責,以知足新的需求,同時又沒有改變系統原來的功能。
可參考以下理解:
(1) 問題:如何分配職責給對象、子系統和系統,使得這些元素中的變化或不穩定的點不會對其餘元素產生不利影響?
(2) 解決方案:找出預計有變化或不穩定的元素,爲其在這些變化以外建立穩定的「接口」而分配職責。
(3) 分析:受保護變化模式簡稱PV,它是大多數編程和設計的基礎,是模式的基本動機之一,它使系統可以適應和隔離變化。它與面向對象設計原則中的開閉原則相對應,即在不修改原有元素(類、模塊、子系統或系統)的前提下擴展元素的功能。開閉原則又可稱爲「可變性封裝原則(Principle of Encapsulation of Variation, EVP)」,要求找到系統的可變因素並將其封裝起來。如將抽象層的不一樣實現封裝到不一樣的具體類中,並且EVP要求儘可能不要將一種可變性和另外一種可變性混合在一塊兒,這將致使系統中類的個數急劇增加,增長系統的複雜度。在具體實現時,爲了符合受保護變化模式,咱們一般須要對系統進行抽象化設計,定義系統的抽象層,再經過具體類來進行擴展。若是須要擴展系統的行爲,無須對抽象層進行任何改動,只須要增長新的具體類來實現新的業務功能便可,在不修改已有代碼的基礎上擴展系統的功能。大多數設計原則和GoF模式都是受保護變化模式的體現。
防止變異(PV)是很是重要和基本的軟件設計原則,幾乎全部的軟件或架構設計技巧都是防止變異的特例。PV是一個根本原則,它促成了大部分編程和設計的模式和機制,用來提供靈活性和防止變化。在軟件設計中,除了數據封裝、接口、多態、間接性等機制是PV的核心機制以外,沒有一種固定的或者是通用的辦法可以防止一切變化的產生。所以PV的實現依賴的是一系列的OO設計方面的經驗性原則,用以產生一個設計良好的高內聚、低耦合的系統,從而支持PV。
關於OCP原則,後面還會有單獨的論述。
這裏咱們能夠看到,由於增長了純虛構類
這樣的設計更符合高內聚和低耦合原則,雖而後來咱們又增長了一個菱形
這裏咱們把兩種不一樣的數據存儲功能分別放在了兩個類裏來實現,這樣若是將來保存到
這裏咱們在訂單類裏增長了一個
這裏由於訂單是商品的容器,也只有訂單持有初始化商品的信息,因此這個耦合關係是正確的且沒辦法避免的,因此由訂單來建立商品。
針對這個問題須要權衡的是,比較商品是否相同的方法須要放到那裏類裏來實現呢?分析業務得知須要根據商品的編號
咱們生活在一個充滿規則的世界裏,在複雜多變的外表下,萬事萬物都被永恆的真理支配並有規律的運行着。模式也是同樣,不論那種模式,其背後都潛藏着一些「永恆的真理」,這個真理就是設計原則。記得一次參加微軟的架構師培訓,期間講到設計模式,有人問了老師一個問題:「什麼東西比設計模式更重要?」,老師是一位有多年豐富實踐經驗的開發者,他絕不猶豫地回答到:「比模式更重要的是原則」。這句話我時常可以想起,愈來愈以爲這是一個偉大的答案。的確,還有什麼比原則更重要呢?就像人的世界觀和人生觀同樣,那纔是支配你一切行爲的根本,而對於設計模式來講,爲何這個模式要這樣解決這個問題,而另外一個模式要那樣,它們背後都遵循的就是永恆的設計原則。能夠說,設計原則是設計模式的靈魂。
對於設計原則的深刻探討我尚未那個深度,推薦你們去看《敏捷軟件開發—原則、模式與實踐》,下面僅對部分經常使用的設計原則作些簡單的講解:
1. 單一職責原則(Single Responsibility Principle, SRP)
「就一個類而言,應該僅有一個引發它變化的緣由。」,這裏的單一職責,或者單一的變化緣由,並不是指嚴格意義上的惟一的一個職責或者緣由,而應該是指一類聯繫緊密的職責,或者說單一的變化動機。SRP實際上是高內聚的一種形式,它要求一個類緊密圍繞着一項職責進行工做,是一種內聚程度較高的設計。也就是說,不要把變化緣由各不相同的職責放在一塊兒,由於不一樣的變化會影響到不相干的職責。再通俗一點地說就是,不應你管的事情你不要管,管好本身的事情就能夠了,多管閒事害了本身也害了別人。(固然這裏說的多管閒事跟見義勇爲是兩回事,咱們提倡見義勇爲!)
例如:參考下圖中的設計,圖形計算程序只使用了正方形的Area()方法,永遠不會使用Draw()方法,而它卻跟Draw方法關聯了起來。這違反了單一原則,若是將來由於圖形繪製程序致使Draw()方法產生了變化,那麼就會影響到原本絕不關係的圖形計算程序。
那麼咱們該怎麼作呢?以下圖,將不一樣的職責分配給不一樣的類,使單個類的職責儘可能單一,就隔離了變化,這樣他們也不會互相影響了。
「軟件實體(類、模塊、函數等)應該是能夠擴展的,可是不可修改。」嘿!多麼樸實的話語,第一次看這個原則的時候我都看傻了,我當時在想「這不是&#%作白日夢嗎!不修改怎麼擴展啊?」可是隨着學習的深刻,理解了這個「不修改」是什麼意思,意思是「你能夠隨便增長新的類,可是不要修改原來的類」。從這個角度去理解就好多了,其實這裏仍是一個隔離變化的問題。
模塊應該同時對擴展、可適應性開放和對影響客戶的更改封閉。也即,模塊應該對擴展開放,對修改關閉。OCP實際上是PV的另外一種描述。除了這些原則以外,還有許多各類各樣的技術能夠用來PV。這裏就再也不贅述。在一個系統中,值得應用PV的地方是可能會產生變化的地方。其中之一是系統的需求定義的一些變化點,好比包含計算消費稅程序的電子商務系統須要支持在新的國家開展業務。那麼在計算消費稅的程序裏使用策略模式便能很好地支持這一點擴展。換句話說,若是需求中明確規定了在這一點上不容許有任何變化(實際上不太可能,這裏只是假設),那麼在計算消費稅的程序裏使用if else語句也何嘗不可。另外,設計者預測出來的可能會產生變化的點也能夠應用PV。仍是說上一個例子。計算消費稅的需求規定了只包含三個國家,可是設計者若是預測出這是一個變化點,可能在未來會有各類變化,那麼設計者一樣能夠利用策略模式來完成這一設計。可是,在預測變化點時,必定要當心謹慎,仔細考慮。不然很容易在系統中預測出大量的、實際上變化機率並不大的變化點出來,從而致使在整個系統中大量地出現了靈活的、使用模式的、支持變化的設計。而每每這些設計在整個軟件的生命週期中都從未被用到過。這種現象被稱爲過分設計。這樣的狀況在軟件開發的過程當中是極其浪費資源的,應該努力去避免這種狀況的發生。
例如:以下圖,有一個客戶端程序經過數據訪問接口操做數據,對於這套系統來講,一開始計劃使用的是SQL Server或Oracle數據庫,可是後來考慮到成本,改用免費的MySQL;那麼對於客戶端程序來講,後來數據的擴展對它沒有任何影響,它在不知不覺間就用上了免費好用的MySQL數據庫,這全要感謝OCP原則。
「抽象不該該依賴於細節。細節應該依賴於抽象。」,關於這個原則,還有種說法是.「高層不該該依賴於底層,二者都應該依賴於抽象。」,其實怎麼說都是對的,關鍵就是要理解一點,只有抽象的東西纔是最穩定的,也就是說,咱們依賴的是它的穩定。若是未來「抽象」也不穩定了,那麼誰穩定我跟誰,其實說白了不就是傍大款嗎!
DIP的核心思想是面向接口編程。一個依賴接口實現的類要比一個依賴細節實現的類更容易維護和擴展。它來源於這樣一個事實:相對於細節的多變性,抽象的東西要穩定得多。好比,在一個系統中,類A依賴於類B實現。那麼,當類A須要改變對類B的依賴,轉而依賴類C時,類A必須修改源代碼才能實現。而若是類A依賴於一個接口X實現,而類B和類C都實現這個接口,那麼以後不管是類B和類C之間的替換,抑或是讓類A去依賴新的類D,都是很是易於實現的。DIP的本質也是下降了耦合,將一個類與細節的耦合下降到了與接口的耦合,而與一個穩定的接口之間耦合是良好的。
例如:參考下圖的設計,一個開關跟燈直接鏈接在一塊兒了,也就是說開關依賴於燈的打開和關閉方法,那麼若是我想用這個開關也能夠打開其餘東西呢,好比電視、音響。顯然這個設計是沒法知足這個要了,由於咱們依賴了細節而不是抽象,這個開關已經等價於「燈的開關」。
那麼咱們該如何來設計一個通用的開關呢?參考下圖的設計,OK!如今咱們不只能夠打開燈,還能夠打開電視和音響,甚至將來任何實現了「開關接口」的任何東西。
「不該該強迫客戶依賴於它們不用的方法。接口屬於客戶,不屬於它所在的類層次結構。類不該該依賴它不須要的接口,一個類對另外一個類的依賴應創建在最小的接口上。」,ISP也是高內聚低耦合的一種表現形式。由於類對公有接口的依賴也是一種導出耦合的關係。若是一個類依賴了它不須要的接口,那麼在系統中便存在了這樣一種沒有意義的耦合,不利於耦合度的下降。反過來,在創建接口時也不要創建臃腫的、包含一切的接口。這樣的接口反而失去了高內聚性。這個說得很明白了,再通俗點說,不要強迫客戶使用它們不用的方法,若是強迫用戶使用它們不使用的方法,那麼這些客戶就會面臨因爲這些不使用的方法的改變所帶來的改變。
例如:參考下圖的設計,在這個設計裏,取款、存款、轉賬都使用一個通用界面接口,也就是說,每個類都被強迫依賴了另兩個類的接口方法,那麼每一個類有可能由於另外兩個類的方法(跟本身無關)而被影響。拿取款來講,它根本不關心「存款操做」和「轉賬操做」,但是它卻要受到這兩個方法的變化的影響,真是土鱉!
那麼咱們該如何解決這個問題呢?參考下圖的設計,爲每一個類都單獨設計專門的操做接口,使得它們只依賴於它們關係的方法,這樣就不會互相影響,也就不會在發生土鱉的事情了!
「子類型必須可以替換掉它們的基類型。」,若是對於類型S的每一個對象o1存在類型T的對象o2,那麼對於全部定義了T的程序P來講,當用o1替換o2而且S是T的子類型時,P的行爲不會改變。也就是說繼承中的「IS A」關係是必須保證的,不然還算什麼繼承啊!將上面的形式化定義換一種簡單的說法就是:在一個系統中,任何類的對象均可以由該類的任何一個子類的任何對象給替換掉,而整個系統的行爲不變。LSP是一種簡單的思想,然而嚴格按照LSP去設計系統會使得全部基類的接口語義是徹底穩定的,這很是有利於系統的擴展性。若是違反了LSP原則,常會致使在運行時(RTTI)的類型判斷違反OCP原則。
例如:函數A的參數是基類型,調用時傳遞的對象是子類型,正常狀況下,增長子類型都不會影響到函數A的,若是違反了LSP,則函數A必須當心的判斷傳進來的具體類型,不然就會出錯,這就已經違反了OCP原則。
不要歷經遠距離的對象結構路徑去向遠距離的間接對象發送消息。
假設在一個系統中,存在A、B、C、D四個類。其中,類A包含了類B,類B包含了類C,類C包含了類D。那麼,一個類A的實例a中必定會存在着一個類D的實例d,這是經過類B和類C間接包含進來的。那麼,LOD禁止a向d直接發送任何消息,由於這直接引入了類A同類D之間毫無道理的耦合;同時這也是一種極爲脆弱的設計,它沒法應對未來可能出現的任何變化,對象路徑越長,其穩定性就越差。LOD要求一個類在其方法裏只給有限的對象發送消息,包括:自身、方法的參數、自身的屬性、做爲自身屬性的集合中的元素、在方法中建立的對象。
深入理解面向對象是學好設計模式的基礎,掌握必定的面向對象設計原則才能掌握面向對象設計模式的精髓,從而實現靈活運用設計模式。僅知道OO的語言機制是不夠的,懂得語言裏的封裝、繼承、多態,只是知足了最最基礎的條件,要真正發揮OO的強大的做用,關鍵是要深入理解以上的GRASP模式和設計原則,在此基礎上去再深刻理解設計模式,並在實踐中不斷磨練。
模式跟OO原則相比其實並不重要,若是你能設計出基本符合以上原則的程序,那麼可能就已經總結出了新的模式,因此學習模式的根本是爲了深刻理解OO思想和原則,使咱們能夠寫出高內聚低耦合的程序。
另外最近在學習李建忠老師的「C#面向對象設計模式縱橫談系列課程」時候,李老師提出了一個「重構到模式」的理論,感受十分有道理,模式不徹底是供咱們套用的模版,在特定的業務環境下,咱們實現的可能只是「相似XX模式」的設計模式,由於針對這個環境,這麼使用就是最合適的,而不是何時都必須徹底照搬GOF的23種設計模式的格式,模式是死的,而人是活的,找到最合適的實現方式就好,不要爲了設計模式而使用設計模式。