背景數據庫
面向對象基礎知識,只是給了咱們一個概念,如何更好的設計出良好的面向對象代碼,須要有設計原則做爲支持。設計原則是核心指導思想,在這些原則的基礎上,通過不斷的實踐,抽象,提煉逐步產生了針對特定問題的設計模式。所以,學好設計模式的基礎是掌握基本的設計原則。本文將介紹面向對象經常使用的設計原則。(某些原則,也能夠用在系統級,模塊級等類型的設計中應用)編程
一、代碼抽象三原則設計模式
1.1 DRY原則(Don't repeat yourself)微信
意思是:不要重複本身。它的涵義是,系統的每個功能都應該有惟一的實現。也就是說,若是屢次遇到相同的問題,就須要抽象出一個通用的解決方案,不要重複開發相同的功能。架構
用代碼舉例:若是兩個地方須要發送短信的功能,第一個功能是發送提醒短信,第二個是發送驗證碼短信。則須要把發送短信的公用代碼進行提煉。框架
1.2 YAGNI原則( You aren't gonna need it)分佈式
意思是:你不會須要它。出自極限編程的原則,指除了核心功能外,其它功能一律不要部署。背後的指導思想是儘快的讓代碼運行起來。簡單理解是儘可能避免沒必要要的代碼,少就是多。好比:過多的日誌打印,過多邏輯檢查,過多的異常處理等,若是能簡化則簡化。ide
1.3Rule Of Three原則模塊化
Rule of three 稱爲"三次原則",指的是當某個功能第三次出現時,才進行"抽象化"。它的含義是:當第一次用到某個功能時,寫一個特定的解決方法;第二次又用到的時候,拷貝上一次的代碼;第三次出現的時候,才着手"抽象化",寫出通用的解決方法。函數
1.4 三原則之間的關係
DRY強調對通用問題的抽象,YAGNI強調快速和簡單。Rule Of Three至關於對前兩個原則作了一個折衷,提出了應用原則的度量。三原則的折中,有如下幾個好處。
(1)省事,避免過分設計:若是隻有一個地方用,就不必過分抽象,避免過分設計。
(2)容易發現模式:問題出現的場景多,容易找到通用的部分,方便進行抽象,進而找到模式。
二、GRASP原則
GRASP(General Responsibility Assignment Software Patterns),中文名稱:「通用職責分配軟件模式」,核心是本身幹本身能幹的事,本身只幹本身的 事,也就是職責的分配和實現高內聚。用來解決面向對象設計的一些問題。GRASP一共包括9種模式,給出了最基本的面向對象指導原則,好比:如何決定一個系統有多少對象,每一個對象都包括什麼職責。
2.1 Infomation Expert(信息專家)
設計類時,若是一個類有完成某個職責的全部信息,則應該把該職責分配給該類。此時,該類至關於該職責的信息專家。
例如: 常見的網上商店的購物車(ShopCar),須要讓每種商品(SKU)只在購物車內出現一次,購買相同商品,只須要更新商品的數量便可。以下圖:
針對這個問題須要權衡的是,比較商品是否相同的方法放到哪一個類裏來實現呢?分析業務得知須要根據商品的編號(SKUID)來惟一區分商品,而商品編號是惟一存在於商品類的,因此根據信息專家模式,應該把比較商品是否相同的方法放在商品類裏。
2.2 Creator(創造者)
用於判斷對象的初始化由哪一個類發起,用於肯定正確的依賴關係。實際應用中,符合下列任一條件的時候,都應該由類 A 來建立類 B,這時 A 是 B 的建立者:
a、A 是 B 的聚合
b、A 是 B 的容器
c、A 持有初始化 B 的信息(數據)
d、A 記錄 B 的實例
e、A 頻繁使用 B
例如:由於訂單(Order)是商品(SKU)的容器,因此應該由訂單來建立商品。以下圖:
這裏由於訂單是商品的容器,也只有訂單持有初始化商品的信息,因此這個耦合關係是正確的且沒有辦法避免的,因此由訂單來建立商品。
2.3 Low coupling(低耦合)
耦合是指兩個類之間的依賴程度。低耦合說明兩個類之間的依賴程度低。好的耦合是低耦合,有如下好處:
(1)低耦合下降了由於一個類的變化,影響其餘類的範圍。
(2)使類之間的關係簡單,更容易理解。
耦合的場景
a、A 是 B 的屬性
b、A 調用 B 的實例的方法
c、A 的方法中引用的 B,例如 B 是 A 方法的返回值或參數。
d、A 是 B 的子類,或者 A 實現 B
例如:Creator 模式的例子裏,實際業務中須要另外一個出貨人來清點訂單(Order)上的商品(SKU),並計算出商品的總價,可是因爲訂單和商品之間的耦合已經存在了,那麼把這個職責分配給訂單更合適,這樣能夠下降耦合,以便下降系統的複雜性。以下圖:
這裏咱們在訂單類裏增長了一個 TotalPrice() 方法來執行計算總價的職責,沒有增長沒必要要的耦合。
2.4 High cohesion(高內聚)
內聚是指類內部職責的緊密程度,高內聚的類是設計良好的類,具有良好的隔離性,當內部變化了,只要接口不改變,不影響其餘部分。
例如:一個訂單數據存取類(OrderDAO),訂單便可以保存爲 Excel 模式,也能夠保存到數據庫中;那麼,不一樣的職責最好由不一樣的類來實現,這樣纔是高內聚的設計,以下圖:
這裏咱們把兩種不一樣的數據存儲功能分別放在了兩個類裏來實現,這樣若是將來保存到 Excel 的功能發生錯誤,那麼就去檢查 OrderDAOExcel 類就能夠了,這樣也使系統更模塊化,方便劃分任務,好比這兩個類就能夠分配到不一樣的人同時進行開發,這樣也提升了團隊協做和開發進度。
2.5 Controller(控制器)
用於接收和處理系統事件的職責,通常配置給能夠表明整個系統的類,通常成功XXController。
有以下原則:
a、系統事件的接收與處理一般由一個高級類來代替。
b、一個子系統會有不少控制類,分別處理不一樣的事務。
在MVC架構中,對應的是C。
2.6 Polymorphism(多態)
面向對象的三大特徵一下,指一個接口能夠有不一樣的實現,用於提升系統的靈活性和擴展性,寫出高內聚,低耦合的代碼。
例如:咱們想設計一個繪畫程序,要支持能夠畫不一樣類型的圖形,咱們定義一個抽象類 Shape,矩形(Rectangle)、圓形(Round)分別繼承這個抽象類,並重寫(override)Shape 類裏的Draw() 方法,這樣咱們就可使用一樣的接口(Shape抽象類)繪製出不一樣的圖形,以下圖:
這樣的設計更符合高內聚和低耦合原則,雖而後來咱們又增長了一個菱形(Diamond)類,對整個系統結構也沒有任何影響,只要增長一個繼承 Shape 類就好了。
2.7 Pure Fabrication(純虛構)
這裏的純虛構跟咱們常說說的純虛構函數意思相近。高內聚低耦合,是系統設計的終極目標,可是內聚和耦合永遠都是矛盾對立的。高內聚覺得這拆分出更多數量的類,可是對象之間須要協做來完成任務,這又形成了高耦合,反過來依然。該如何解決這個矛盾呢?這個時候就須要純虛構模式,由一個純虛構的類來協調內聚和耦合,能夠在必定程度上解決上述問題。
例如:上面多態模式的例子,若是咱們的繪圖程序須要支持不一樣的系統,那麼由於不一樣系統的API結構不一樣,繪圖功能也須要不一樣的實現方式,那麼該如何設計更合適呢?以下圖:
這裏咱們能夠看到,由於增長了純虛構類AbstractShape,不管是哪一個系統均可以經過AbstractShape 類來繪製圖形,咱們即沒有下降原來的內聚性,也沒有增長過多的耦合,可謂魚肉和熊掌兼得。
2.8 Indirection(間接)
用於隔離或組合兩個類之間的交互,避免兩個或多個事物之間直接耦合,好比用戶類和商品類,二者的職責不一樣,若是之間進行直接調用,會造成強耦合(多對多關係),則能夠增長一箇中間者,實現對調用用戶類和商品類的聚合處理。
2.9 Protected Variations(防止變異)
如何設計對象、系統和子系統,使其內部的變化或者不穩定因素不會對其餘元素產生不良影響?
預先識別不穩定的因素,抽象爲接口,針對接口編程,隔離變化,若是將來發生變化,能夠經過接口擴展新功能。例如:訂單支付中的支付方式是不穩定的,剛開始有網銀,快捷,以後又有了微信,支付寶。在設計時能夠抽象出支付接口,當增長微信時,增長微信實現便可。
三、SOLID原則
3.1 單一職責原則(Single Responsibility Principle - SRP)
一個類只有一個引發變化的緣由。若是一個類有多個引發變化的緣由,當其中一個變化時會影響到其餘代碼。這樣代碼的內聚性很差,會致使維護性變差,複用性下降。
用於指導對類的設計,只有一個引發變化的緣由,單一職責,設計出高內聚的類(或方法等元素)。
3.2 開放封閉原則(Open Closed Principle - OCP)
對於軟件實體應該對擴展開放,對修改關閉。對擴展開放,是指當有新需求或需求變化時,能夠僅對代碼進行擴展,就能夠適應新的需求。對修改關閉是指,類或方法一旦設計完成,就不須要對其進行修改。
實現開閉原則的基礎時,找到變化,封裝變化。
用於指導可擴展的設計。
3.3 里氏替換原則(Liskov Substitution Principle - LSP)
一個軟件實體若是使用的是基類的話, 那麼也必定適用於其子類, 並且它根本覺察不錯使用的是基類對象仍是子類對象;反過來的代換這是不成立的,
用於指導繼承體系的設計。子類出現的子類,父類能夠替換,在子類設計時能夠擴展父類的功能,但不能改變父類原有的功能。
3.4 最少知識原則(Least Knowledge Principle - LKP)
最少知識原則又叫迪米特法則。一個實體應當儘可能少的與其餘實體之間發生相互做用,使得系統功能模塊相對獨立。也就是說一個軟件實體應當儘量少的與其餘實體發生相互做用。當一個模塊修改時,儘可能少的影響其餘的模塊,容易擴展,這是對軟件實體之間通訊的限制,要求限制軟件實體之間通訊的寬度和深度。
用於指導類之間的關係(通訊)設計。
3.5 接口隔離原則(Interface Segregation Principle - ISP)
接口隔離原則的含義是:創建單一接口,不要創建龐大臃腫的接口,儘可能細化接口,接口中的方法儘可能少。
用於指導接口的設計,對接口進行約束。
(1)接口儘可能單一,但要適度,避免過多的接口類定義。
(2)實現類只實現須要的接口便可,當一個類實現多個接口時,調用時在具體場景只使用單一接口便可,把沒必要要的隱藏起來。這樣依賴關係是最小的。有利於控制變化。
3.6 依賴倒置原則(Dependence Inversion Principle - DIP)
依賴倒置原則的核心思想是面向接口編程,不該該面向實現類編程。
(1)抽象不該該依賴於細節。細節應該依賴於抽象。
(2)高層不該該依賴於底層,二者都應該依賴於抽象。
用於指導抽象設計,依賴穩定的,將穩定的進行抽象。
四、其餘設計原則
4.1 組合/聚合複用原則(Composition/Aggregation Reuse Principle - CARP)
在設計中,優先考慮使用組合,而不是繼承。繼承容易產生反作用,組合具備更好的靈活性。如:代理模式、裝飾模式、適配器模式等。
4.2 無環依賴原則(Acyclic Dependencies Principle - ADP)
當 A 模塊依賴於 B 模塊,B 模塊依賴於 C 模塊,C 依賴於 A 模塊,此時將出現循環依賴。在設計中應該避免這個問題,可經過引入「中介者模式」解決該問題。
4.3 共同封裝原則(Common Closure Principle - CCP)
將易變的類放在同一個包裏,將變化隔離出來。該原則是「開放-封閉原則」的延生。
4.4 共同重用原則(Common Reuse Principle - CRP)
若是重用了包中的一個類,那麼也就至關於重用了包中的全部類,咱們要儘量減少包的大小。
4.5 好萊塢原則(Hollywood Principle - HP)
好萊塢明星的經紀人通常都很忙,他們不想被打擾,每每會說:Don't call me, I'll call you. 翻譯爲:不要聯繫我,我會聯繫你。對應於軟件設計而言,最著名的就是「控制反轉」(或稱爲「依賴注入」),咱們不須要在代碼中主動的建立對象,而是由容器幫咱們來建立並管理這些對象。
4.6 保持它簡單與傻瓜(Keep it simple and stupid - KISS)
不要讓系統變得複雜,界面簡潔,功能實用,操做方便,要讓它足夠的簡單,足夠的傻瓜。
4.7 慣例優於配置(Convention over Configuration - COC)
儘可能讓慣例來減小配置,這樣才能提升開發效率,儘可能作到「零配置」。不少開發框架都是這樣作的。
4.8 命令查詢分離(Command Query Separation - CQS)
在定義接口時,要作到哪些是命令,哪些是查詢,要將它們分離,而不要揉到一塊兒。在讀寫分離或分佈式系統中應用較多。
4.9 關注點分離(Separation of Concerns - SOC)
將一個複雜的問題分解爲多個簡單的問題,而後逐個解決簡單的問題,那麼複雜的問題就解決了。
4.10 契約式設計(Design by Contract - DBC)
模塊或系統之間的交互,都是基於契約(接口或抽象)的,而不要依賴於具體實現。該原則建議咱們要面向契約編程。
小結
本文介紹了經常使用的設計原則,基於這些原則,能夠用於指導代碼設計,架構評審,Code Review等。在設計原則的基礎上產生了設計模式,下一篇,咱們會總體介紹GOF 23種設計模式。