面向物件設計與開發原則
介紹
這裡介紹了5個面向物件設計與開發原則–SOLID原則,分別是:單一職責原則、開放封閉原則、里氏替換原則、介面隔離原則、依賴倒置原則。另外還介紹了其他3個原則:迪米特法則、"Tell, Don’t ask"原則、單一抽象層次原則。在面向物件的程式設計和開發過程中時,這些原則非常重要,我們應該儘量去遵守它。
-
SRP(The Single Responsibility Principle) 單一職責原則
-
OCP(The Open Closed Principle) 開放封閉原則
-
LSP(The Liskov Substitution Principle) 里氏替換原則
-
ISP(The Interface Segregation Principle) 介面隔離原則
-
DIP(The Dependency Inversion Principle) 依賴倒置原則
-
LoD(The Law of Demeter) 迪米特法則
-
“Tell, Don’t ask” "Tell, Don’t ask"原則
-
SLAP(Single Level of Abstrction Principle) 單一抽象層次原則
單一職責原則
宣告:
A class should have only one reason to change.
一個類應該僅有一個引起它變化的原因。
解釋:
SRP是所有原則中最簡單的一個,概念簡潔易懂。但卻也是最難正確運用,或者說是最容易過度使用的原則。
一個類或者一個方法只做一件事。如果一個類承擔的職責過多,就等於把這些職責耦合在一起了,一個職責的變化就可能抑制或者削弱這個類完成其他職責的能力。所以正確的做法是發現職責並把這些職責相互分離。在實際的設計和開發過程中,一定要把握好分離“單個”職責的度,過度的分離只能適得其反,違背了面向物件的封裝思想。
例如:餐廳服務員負責把訂單交給廚師去做,而不是服務員又要處理訂單又要炒菜。
開放封閉原則
宣告:
Software entities(classes,modules,functions,etc.) should be open for extension, but closed for modification.
軟體實體(如類、模組、函式等)應該對擴充套件開放,對修改關閉。
解釋:
OCP是整個面向物件設計的核心。它代表了靈活性、可重用性和可維護性。
對擴充套件開放,對修改關閉。意為一個類實現後就不應該去修改它,而是以擴充套件的方式適應新需求。通過建立穩定的抽象基類或介面,並將具體的行為實現為派生類或實現類,這種做法能很好的解決開放擴充套件的問題。
例如:一開始做了普通計算器程式,突然新增新需求,要再做一個程式設計師計算器,這時不應該修改普通計算器內部,應該使用面向介面程式設計,組合實現擴充套件。
里氏替換原則
宣告:
Derived types must be completely substitutable for their base types.
子型別必須能夠完全的替換掉它們的基型別。
解釋:
LSP是使OCP成為可能的主要原則之一,也是繼承層次特徵的主要設計原則。
所有基類出現的地方都可以用子類替換而不會程式產生錯誤。子類可以擴充套件基類的功能,但不能改變基類原有的功能。
例如:機動車必須有輪胎和發動機,子類寶馬和賓士不應該改寫成沒輪胎或者沒發動機。
介面隔離原則
宣告:
Clients should not be forced to depend upon interfaces that they don’t use.
不應該強迫客戶程式依賴並未使用的介面。
解釋:
ISP有效的降低了程式間的耦合性,提高了程式集的內聚性。避免了介面汙染。
客戶程式應該僅僅依賴於他們實際呼叫的方法。分離客戶就是分離介面。
類不應該依賴不需要的介面,知道越少越好。通過把胖類的介面分解為多個特定於客戶程式的介面,可以實現這個目標。有效的解除了客戶程式和它們沒有呼叫到的方法間的依賴關係。
例如:電話介面只約束接電話和掛電話,不需要讓依賴者知道還有通訊錄。
依賴倒置原則
宣告:
A. High-level modules should not depend on low-level modules. Both should depend on abstractions.
高層模組不應該依賴於低層模組,兩者都應該依賴於抽象。
B. Abstractions should not depend on details. Details should depend on abstractions.
抽象不應該依賴於細節,細節應該依賴於抽象。
解釋:
依賴倒置原則是實現許多面向物件技術所宣稱的好處的基本低層機制,他的正確應用對於建立可重用的框架來說是必須的。同時它對於構建在變化面前富有彈性的程式碼也是非常重要的,由於抽象和細節彼此隔離,所以程式碼也非常容易維護。
針對介面程式設計,而不要對實現程式設計。倒置介面的所有權。高階模組不應該依賴低階模組,而是依賴抽象。抽象不能依賴細節,細節要依賴抽象。
例如:類A內有類B物件,稱為類A依賴類B,但是不應該這樣做,而是選擇類A去依賴抽象。例如:垃圾收集器不管垃圾是什麼型別,要是垃圾就行。
迪米特法則
宣告:
A. Each unit should have only limited knowledge about other units: only units “closely” related to the current unit.
每一個軟體單位對其他的單位都只有最少的知識,而且侷限於那些與本單位密切相關的軟體單位。
B. Each unit should only talk to its friends; don’t talk to strangers.
C. Only talk to your immediate friends.
解釋:
迪米特法則又稱為最少知識原則(Least Knowledge Principle 簡稱LKP)。主要有三層意思,就是讓呼叫者對於目標物件的知識最少,而且只和你的直接朋友對話,千萬不要和陌生人說話。其初衷在於降低類之間的耦合。
例如:
類C的方法f只應該呼叫以下物件的方法:
類C本身(this物件)的方法
類C成員物件的方法
在方法f中建立的物件的方法
作為引數傳遞給f的物件的方法
方法不應呼叫由任何函式返回的物件的方法。
“只跟朋友談話,不與陌生人談話”
"Tell, Don’t ask"原則
宣告:
Procedural code gets information then makes decisions. Object-oriented code tells objects to do things.
過程式程式獲取資訊然後決策;OO程式則告訴物件做某事情。
解釋:
你應該儘量告訴物件你希望它們去做的事情;而不要詢問它們的狀態之後做出決定,最後才告訴它們做什麼事情。
示例:
# 違反“Tell,Don’t ask”原則,詢問物件狀態再做判斷
...
if (person.getAddress().getCountry().equals(”Australia”))
...
# 遵循“Tell,Don’t Ask”原則的程式碼,告訴物件去判斷
...
if (person.livesIn(”Australia”))
...
單一抽象層次原則
宣告:
讓一個方法中所有的操作處於相同的抽象層。
解釋:
我認為統一抽象層次原則是最重要的優秀編碼原則,沒有之一。需要熟練掌握各種設計模式、設計原則,具備優秀編碼態度,長時間學習實踐積累優秀編碼技能,養成優秀編碼習慣後才能做到這一點。
SLAP原則是指讓一個方法中所有的操作處於相同的抽象層。否則跳躍的程式碼的抽象層次破壞了程式碼的流暢性。
如下所示:
示例1:方法的操作不在同一個抽象層次,前後是抽象,中間是細節。
void compute()
{
input();
flags = 0x0080;
output();
}
示例2:方法的操作在同一個抽象層次上
void compute()
{
input();
process();
output();
}
小結
這些原則是數十年軟體工程經驗來之不易的結果。是眾多開發人員和研究人員思想和著作的結晶。
我們應該牢記面向物件設計的原則,理解OO的設計思路。