1. 程式人生 > 其它 >面向物件設計的七大原則

面向物件設計的七大原則

面向物件設計的七大原則

面向物件設計原則簡介

常用的面向物件設計原則包括7個,這些原則並不是孤立存在的,它們相互依賴,相互補充。

設計原則名稱 設計原則簡介
單一職責原則(Single Responsibility Principle, SRP) 類的職責要單一,不能將太多的職責放在一個類中
開閉原則(Open-Closed Principle, OCP) 軟體實體對擴充套件是開放的,但對修改是關閉的,即在不修改一個軟體實體的基礎上去擴充套件其功能
里氏代換原則(Liskov Substitution Principle, LSP) 在軟體系統中,一個可以接受基類物件的地方必然可以接受一個子類物件
依賴倒轉原則(Dependency Inversion Principle, DIP) 要針對抽象層程式設計,而不要針對具體類程式設計
介面隔離原則(Interface Segregation Principle, ISP) 使用多個專門的介面來取代一個統一的介面
合成複用原則(Composite Reuse Principle, CRP) 在系統中應該儘量多使用組合和聚合關聯關係,儘量少使用甚至不使用繼承關係
迪米特法則(Law of Demeter, LoD) 一個軟體實體對其他實體的引用越少越好,或者說如果兩個類不必彼此直接通訊,那麼這兩個類就不應當發生直接的相互作用,而是通過引入一個第三者發生間接互動

軟體的可維護性和可複用性

軟體的複用(Reuse)或重用擁有眾多優點,如可以提高軟體的開發效率,提高軟體質量,節約開發成本,恰當的複用還可以改善系統的可維護性。

面向物件設計複用的目標在於實現支援可維護性的複用

在面向物件的設計裡面,可維護性複用都是以面向物件設計原則為基礎的,這些設計原則首先都是複用的原則,遵循這些設計原則可以有效地提高系統的複用性,同時提高系統的可維護性。

面向物件設計原則和設計模式也是對系統進行合理重構的指南針,重構(Refactoring)是在不改變軟體現有功能的基礎上,通過調整程式程式碼改善軟體的質量、效能,使其程式的設計模式和架構更趨合理,提高軟體的擴充套件性和維護性。


1、單一職責原則

定義

一個物件應該只包含單一的職責,並且該職責被完整地封裝在一個類中。

就一個類而言,應該僅有一個引起它變化的原因

分析

一個類(或者大到模組,小到方法)承擔的職責越多,它被複用的可能性越小,而且如果一個類承擔的職責過多,就相當於將這些職責耦合在一起,當其中一個職責變化時,可能會影響其他職責的運作。

類的職責主要包括兩個方面:資料職責和行為職責,資料職責通過其屬性來體現,而行為職責通過其方法來體現

單一職責原則是實現高內聚、低耦合的指導方針,在很多程式碼重構手法中都能找到它的存在,它是最簡單但又最難運用的原則,需要設計人員發現類的不同職責並將其分離,而發現類的多重職責需要設計人員具有較強的分析設計能力和相關重構經驗。


2、開閉原則

定義

一個軟體實體應當對擴充套件開放,對修改關閉。也就是說在設計一個模組的時候,應當使這個模組可以在不被修改的前提下被擴充套件,即實現在不修改原始碼的情況下改變這個模組的行為。

分析

開閉原則由 Bertrand Meyer 於1988年提出,它是面向物件設計中最重要的原則之一。

在開閉原則的定義中,軟體實體可以指一個軟體模組、一個由多個類組成的區域性結構或一個獨立的類

抽象化是開閉原則的關鍵。

開閉原則還可以通過一個更加具體的“對可變性封裝原則”來描述,對可變性封裝原則(Principle of Encapsulation of Variation, EVP)要求找到系統的可變因素並將其封裝起來。


3、里氏代換原則

定義

如果對每一個型別為 S 的物件 o1 ,都有型別為 T 的物件 o2 ,使得以 T 定義的所有程式 P 在所有的物件 o1 都代換成 o2 時,程式 P 的行為沒有變化,那麼型別 S 是型別 T 的子型別。

所有引用基類(父類)的地方必須能透明地使用其子類的物件。

分析

里氏代換原則由2008年圖靈獎得主、美國第一位電腦科學女博士、麻省理工學院教授 Barbara Liskov 和卡內基.梅隆大學 Jeannette Wing 教授於1994年提出。

里氏代換原則可以通俗表述為:在軟體中如果能夠使用基類物件,那麼一定能夠使用其子類物件。把基類都替換成它的子類,程式將不會產生任何錯誤和異常,反過來則不成立,如果一個軟體實體使用的是一個子類的話,那麼它不一定能夠使用基類。

里氏代換原則是實現開閉原則的重要方式之一,由於使用基類物件的地方都可以使用子類物件,因此在程式中儘量使用基類型別來對物件進行定義,而在執行時再確定其子類型別,用子類物件來替換父類物件。


4、依賴倒轉原則

定義

高層模組不應該依賴低層模組,它們都應該依賴抽象抽象不應該依賴於細節,細節應該依賴於抽象

針對介面程式設計,不要針對實現程式設計

分析

依賴倒轉原則是 Robert C. Martin 在1996年為《C++ Reporter》所寫的專欄 Engineering Notebook 的第三篇,後來加入到他在2002年出版的經典著作《Agile Software Development, Principles, Patterns, and Practices》中。

簡單來說,依賴倒轉原則就是指:程式碼要依賴於抽象的類,而不要依賴於具體的類;要針對介面或抽象類程式設計,而不是針對具體類程式設計。

實現開閉原則的關鍵是抽象化,並且從抽象化匯出具體化實現,如果說開閉原則是面向物件設計的目標的話,那麼依賴倒轉原則就是面向物件設計的主要手段

依賴倒轉原則的常用實現方式之一是在程式碼中使用抽象類,而將具體類放在配置檔案中

類之間的耦合有零耦合關係、具體耦合關係和抽象耦合關係。依賴倒轉原則要求客戶端依賴於抽象耦合,以抽象方式耦合是依賴倒轉原則的關鍵

依賴注入:

(1)構造注入(Constructor Injection):通過建構函式注入例項變數。

(2)設值注入(Setter Injection):通過Setter方法注入例項變數。

(3)介面注入(Interface Injection):通過介面方法注入例項變數。


5、介面隔離原則

定義

客戶端不應該依賴那些它不需要的介面。注意,在該定義中的介面指的是所定義的方法。

一旦一個介面太大,則需要將它分割成一些更細小的介面,使用該介面的客戶端僅需知道與之相關的方法即可。

分析

介面隔離原則是指使用多個專門的介面,而不使用單一的總介面。每一個介面應該承擔一種相對獨立的角色,不多不少,不幹不該乾的事,該乾的事都要幹。

(1) 一個介面就只代表一個角色,每個角色都有它特定的一個介面,此時這個原則可以叫做“角色隔離原則”。

(2) 介面僅僅提供客戶端需要的行為,即所需的方法,客戶端不需要的行為則隱藏起來,應當為客戶端提供儘可能小的單獨的介面,而不要提供大的總介面。

使用介面隔離原則拆分介面時,首先必須滿足單一職責原則,將一組相關的操作定義在一個介面中,且在滿足高內聚的前提下,介面中的方法越少越好。

可以在進行系統設計時採用定製服務的方式,即為不同的客戶端提供寬窄不同的介面,只提供使用者需要的行為,而隱藏使用者不需要的行為。


6、合成複用原則

定義

儘量使用物件組合,而不是繼承來達到複用的目的。

分析

合成複用原則就是指在一個新的物件裡通過關聯關係(包括組合關係和聚合關係)來使用一些已有的物件,使之成為新物件的一部分;新物件通過委派呼叫已有物件的方法達到複用其已有功能的目的。簡言之:要儘量使用組合/聚合關係,少用繼承

在面向物件設計中,可以通過兩種基本方法在不同的環境中複用已有的設計和實現,即通過組合/聚合關係或通過繼承

(1)繼承複用:實現簡單,易於擴充套件。破壞系統的封裝性;從基類繼承而來的實現是靜態的,不可能在執行時發生改變,沒有足夠的靈活性;只能在有限的環境中使用。

(2)組合/聚合複用:耦合度相對較低,選擇性地呼叫成員物件的操作;可以在執行時動態進行。

組合/聚合可以使系統更加靈活,類與類之間的耦合度降低,一個類的變化對其他類造成的影響相對較少,因此一般首選使用組合/聚合來實現複用;其次才考慮繼承。

在使用繼承時,需要嚴格遵循里氏代換原則,有效使用繼承會有助於對問題的理解,降低複雜度,而濫用繼承反而會增加系統構建和維護的難度以及系統的複雜度,因此需要慎重使用繼承複用


7、迪米特法則

定義

(1)不要和“陌生人”說話。

(2)只與你的直接朋友通訊。

(3)每一個軟體單位對其他的單位都只有最少的知識,而且侷限於那些與本單位密切相關的軟體單位。

分析

迪米特法則來自於1987年秋美國東北大學(Northeastern University)一個名為 “Demeter” 的研究專案。

簡單地說,迪米特法則就是指一個軟體實體應當儘可能少的與其他實體發生相互作用。這樣,當一個模組修改時,就會盡量少的影響其他的模組,擴充套件會相對容易,這是對軟體實體之間通訊的限制,它要求限制軟體實體之間通訊的寬度和深度。

在迪米特法則中,對於一個物件,其朋友包括以下幾類:

(1)當前物件本身(this);

(2)以引數形式傳入到當前物件方法中的物件;

(3)當前物件的成員物件;

(4)如果當前物件的成員物件是一個集合,那麼集合中的元素也都是朋友;

(5)當前物件所建立的物件。

任何一個物件,如果滿足上面的條件之一,就是當前物件的“朋友”,否則就是“陌生人”。

迪米特法則可分為狹義法則和廣義法則。在狹義的迪米特法則中,如果兩個類之間不必彼此直接通訊,那麼這兩個類就不應當發生直接的相互作用,如果其中的一個類需要呼叫另一個類的某一個方法的話,可以通過第三者轉發這個呼叫

狹義的迪米特法則:可以降低類之間的耦合,但是會在系統中增加大量的小方法並散落在系統的各個角落,它可以使一個系統的區域性設計簡化,因為每一個區域性都不會和遠距離的物件有直接的關聯,但是也會造成系統的不同模組之間的通訊效率降低,使得系統的不同模組之間不容易協調。

廣義的迪米特法則:指對物件之間的資訊流量、流向以及資訊的影響的控制,主要是對資訊隱藏的控制。資訊的隱藏可以使各個子系統之間脫耦,從而允許它們獨立地被開發、優化、使用和修改,同時可以促進軟體的複用,由於每一個模組都不依賴於其他模組而存在,因此每一個模組都可以獨立地在其他的地方使用。一個系統的規模越大,資訊的隱藏就越重要,而資訊隱藏的重要性也就越明顯。

迪米特法則的主要用途在於控制資訊的過載:

(1)在類的劃分上,應當儘量建立鬆耦合的類,類之間的耦合度越低,就越有利於複用,一個處在鬆耦合中的類一旦被修改,不會對關聯的類造成太大波及。

(2)在類的結構設計上,每一個類都應當儘量降低其成員變數和成員函式的訪問許可權

(3)在類的設計上,只要有可能,一個型別應當設計成不變類

(4)在對其他類的引用上,一個物件對其他物件的引用應當降到最低


總結

對於面向物件的軟體系統設計來說,在支援可維護性的同時,需要提高系統的可複用性。

軟體的複用可以提高軟體的開發效率,提高軟體質量,節約開發成本,恰當的複用還可以改善系統的可維護性。

  • 單一職責原則要求在軟體系統中,一個類只負責一個功能領域中的相應職責。

  • 開閉原則要求一個軟體實體應當對擴充套件開放,對修改關閉,即在不修改原始碼的基礎上擴充套件一個系統的行為。

  • 里氏代換原則可以通俗表述為在軟體中如果能夠使用基類物件,那麼一定能夠使用其子類物件。

  • 依賴倒轉原則要求抽象不應該依賴於細節,細節應該依賴於抽象;要針對介面程式設計,不要針對實現程式設計。

  • 介面隔離原則要求客戶端不應該依賴那些它不需要的介面,即將一些大的介面細化成一些小的介面供客戶端使用。

  • 合成複用原則要求複用時儘量使用物件組合,而不使用繼承。

  • 迪米特法則要求一個軟體實體應當儘可能少的與其他實體發生相互作用。