1. 程式人生 > 程式設計 >GRASP設計原則

GRASP設計原則

GRASP設計原則

GRASP(General Responsibility Assignment Software Pattern)是通用職責分配軟體設計模式。
它由《UML和模式應用》(Applying UML and Patterns)一書作者Craig Larman提出。在面向物件設計的過程中一般的通用方式是構思物件的職責、角色和協作。通常來說,我們在編碼過程中先分析問題域,從中抽象出物件解決問題。簡單的面向物件和優良的面向物件設計的區別在於將如何更合理的劃分物件的角色,給物件賦予合理的職責以及物件之間的互動關係。

在我所理解的GRASP九大原則、SOLID七大原則、GOF23種設計模式這三類設計原則模式中。GRASP處於最上層,SOLID基於它再進一步細化闡述,GOF再根據這些原則進一步的歸納出更具體的模式。 GoF模式是針對特定問題而提出的解決方案,而GRASP站在一個更高的角度來看待面向物件軟體的設計,它是GoF設計模式的基礎。GRASP是物件職責分配的基本原則,其核心思想是職責分配,用職責設計物件。

資訊專家模式 (Information Expert)

定義:

如果某個類擁有執行某個職責所必需的的資訊的話,那麼將這個職責分配給這個類。

理解:

當我們不確定某個職責該分配給類A還是類B的時候,我們可以遵循這個原則。這個設計原則和單一設計原則不同,單一職責原則考慮的是單個類中的職責是否都屬於一類職責。而資訊專家模式考慮則是該把該同一類職責放進類A還是類B中。假設我們有一個長方形Rectangle類(類中有width和height屬性)和一個Measure類,我們應該把getArea()方法放進Rectangle中去,還是將width和height引數傳給Measure類,在Measure中實現getArea()呢?依照該準則,既然Rectangle方法已經有了實現getArea()所必須的屬性的話,那麼就把該把getArea()方法放進Retangle類中。同理如果有一個計算屬性呢?假設是長寬高比例widthHeightRatio的話,也遵循該原則。==這個原則和DDD設計思想當中的充血模型是一致的,大家可以瞭解一下。==

創造者(Creator)

定義:

如果符合下面的一個或者多個條件,則可將建立類A例項的職責分配給類B

  • B包含A;
  • B聚合A;
  • B擁有初始化A的資料並在建立類A的例項時將資料傳遞給類A;
  • B記錄A的例項;
  • B頻繁使用A。

理解:

在面向物件的設計當中,無法避免去建立物件。假設物件B建立物件A,那麼物件B就產生了與物件A的耦合。而這種耦合是無法消除的,即使你將建立物件A的職責分配給物件C,這種耦合還是存在的,只是從物件B轉移到物件C上,系統內還是依然存在這個耦合,無法避免。那麼當我們無法消除耦合的時候,我們應該考慮的是如何降低這個耦合的耦合度。這個原則給出了指導方針。以上的幾個條件潛在的表明了,其實B已經對A有了耦合,既然B已經存在了對A的耦合,那麼我們不妨再將建立A的職責分配給他。這樣分配的話,系統內僅存在一個A與B的耦合。如果將建立A的職責分配給C的話,那麼系統內就會存在B與A(B包含A、B頻繁使用A等條件)和C與A這兩個耦合。在JDK的Map中Entry類的建立職責就分配給了持有它的Map。還有一個比較經典的例子就是Order建立SKU。

低耦合(Low coupling)

定義:

耦合是評價一個系統中各個元素(類、模組、子系統)之間依賴的程度,而良好的設計應該儘可能降低這個程度。
以下是一些耦合關係的體現:

  • A具有一個B型別的屬性;
  • A呼叫B的方法;
  • A的方法包含對B的引用,如方法引數型別為B或返回型別為B;
  • A是B的直接或者間接子類;
  • B是一個介面,A實現了該介面。

理解:

在以上的這些耦合條件中,出現得越多代表耦合程度越高。這些條件簡單籠統的來說就是A對B的“感知”。這種感知體現在物件屬性、方法引數、方法返回值以及介面上面。高耦合的類過多地依賴其他類,這種設計將會導致:一個類的修改導致其他類產生較大影響,系統難以維護和理解。在重用一個高耦合的類時不得不重用它所依賴的其他類,系統重用性差。如何降低耦合的程度有以下一些方法:儘量減少對其他類的引用,提高方法和屬性的訪問許可權,儘量使用組合/聚合原則來替代繼承。 其實面向物件程式設計中的多型就是一種降低型別耦合的方法,如果沒有多型的話,我們的方法需要知道所有子型別別,而多型的話只需要知道父類即可。降低了型別耦合。

高內聚(High cohesion)

定義:

即功能性緊密相關的職責應該放在一個類裡,並共同完成有限的功能。這點與SOLID原則當中的單一職責和介面隔離是一致的。

理解:

很直觀的例子就是,如果類的功能都是高內聚並職責單一的,類的複雜性就降低了,複雜性降低導致維護的成本也就降低了。在傳統的Dao設計模式當中,我們應該儘量拆分細粒度職責單一的Dao供Service進行呼叫。在Service當中,哪一類的資料操作呼叫哪一個Dao就顯而易見,並且單個Dao不會太過膨脹導致維護性變差。高內聚也代表了高隔離,高隔離就意味著,在修改某一個方法的時候,不至於影響到太多其他類。

控制器 (Controller)

定義:

把接收或者處理系統事件訊息的職責分配給一個類。這個類可以代表:整個系統、裝置或者子系統;系統事件發生時對應的用例場景,在相同的用例場景中使用相同的控制器來處理所有的系統事件。

理解:

一個控制器是負責接收或者處理事件的元件物件。MVC模式中的C就是控制器模式。而一個控制器應該處理一類事件。例如我們專案中經常會有的UserController就承擔新增使用者,刪除使用者的事件。一個子系統需要定義多個控制器,分別對應不同的事件處理。一般來說,控制器應當把要完成的功能委託給Service或者其他業務處理物件,它只負責協調和控制業務流程,儘量不要包含太多業務邏輯。

多型(Polymorphism)

定義:

當相關選擇或行為隨型別(類)變化而變化時,用多型操作為行為變化的型別分配職責。

理解:

在面向物件的設計當中經常要根據物件的型別來進行對應的操作。假設我們有一個畫圖Draw類,有多個圖形類Rectangle、Circle、Square。如果要按照不同圖形類進行繪製的話,就需要在Draw類的方法中使用if-else的程式結構,依次判斷型別進行繪製。如果新增一個圖形類的話,就又需要對這段程式碼進行更改。這就違反了開閉原則。而採用多型的形式,將繪製的具體步驟交給圖形類的子類實現。就不用使用if-else的程式結構,在新增圖形類的時候也不需要修改Draw類。通過引入多型,子類物件可以覆蓋父類物件的行為,更好地適應變化。策略模式、工廠方法模式就是關於多型比較好的例子。

純虛構(Pure Fabrication)

定義:

將一組高內聚的職責分配給一個虛構的或處理方便的“行為”類,它並不是問題域中的概念,而是虛構的事物,以達到支援高內聚、低耦合和複用。

理解:

OO設計中的領域模型是對領域內的概念或現實世界中的物件的模型化表示。建立領域模型的關鍵思想是減小軟體人員的思維與軟體模式之間的表示差異。因此,在OO設計時,系統內的大多數類都是來源於現實世界中的真實類。然而,在給這些類分配職責時,有可能會遇到一些很難滿足低耦合高內聚的設計原則。純虛構模式對這一問題給出的方案是:給人為製造的類分配一組高內聚的職責,該類並不代表問題領域的概念,而代表虛構出來的事物。比較明顯的一個例子就是介面卡模式,通過虛構出介面卡這麼一個概念來解耦兩個物件之間的耦合。
許多專案都需要對資料庫進行操作,將系統中的一些物件進行持久化。資訊專家模式給出的建議是將持久化的職責分配給具體的每一個模型類。但是這種建議已經被證明是不符合高內聚低耦合原則的。於是,現在的做法往往會在專案中加入類似於DAO或者Repository這樣的類。這些類在領域模型中是並不存在的。

間接(Indirection)

定義:

分配職責給中間物件以協調元件或服務之間的操作,使得它們不直接耦合。中間物件就是在其他元件之間建立的中介。

理解:

“中介”簡單來說就是通過一箇中間人來處理一件事。本來直接聯絡的兩個物件可以通過另一箇中間物件進行互動,這樣做便實現了隔離和解耦,一個物件的變動不會影響另一個物件,僅會影響到中間物件。在設計模式當中的介面卡模式,橋接模式都採用了一箇中間物件來進行解耦。

受保護變化(Protected Variations)

定義:

找出預計有變化或不穩定的元素,為其建立穩定的“介面”而分配職責。

理解:

受保護變化模式簡稱PV,它是大多數程式設計和設計的基礎,是模式的基本動機之一,它使系統能夠適應和隔離變化。它與面向物件設計原則中的開閉原則相對應,即在不修改原有元素(類、模組、子系統或系統)的前提下擴充套件元素的功能。一個比較顯而易見的例子就是策略模式,通過建立策略介面並定義其中的抽象方法來應對可能出現的新策略。將來有新的策略的時候不需要改變原始碼,而是新建實現策略介面的類。

轉自我的個人部落格 vc2x.com