【設計模式】面向對象設計七大原則

【設計模式】面向對象設計七大原則
【設計模式】設計模式概念和分類
【設計模式】創建型設計模式:單例模式

單一職責原則

Single-Responsibilitiy Principle(SRP):最簡單的設計原則,用於控制類的粒度大小。一個對象應該只包含單一的職責,並且該職責被完整地封裝在一個類中。

一個類(大到模塊,小到方法)承擔的職責越多,它被複用的可能性就越小,而且一個類承擔的職責過多,就相當於將這些職責耦合在一起,當其中一個職責變化時,可能會影響其他職責的運作,因此要將這些職責進行分離,將不同的職責封裝在不同的類中,即將不同的變化原因封裝在不同的類中,如果多個職責總是同時發生改變則可將它們封裝在同一類中。

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

開放封閉原則

Open-Close Principle(OCP):一個軟件實體如類、模塊和函數應該對擴展開放,對修改關閉。目的就是保證程序的擴展性好,易於維護和升級。開放封閉原則簡稱爲開閉原則

爲了滿足開閉原則,需要對系統進行抽象化設計,抽象化是開閉原則的關鍵。在Java、C#等編程語言中,可以爲系統定義一個相對穩定的抽象層,而將不同的實現行爲移至具體的實現層中完成。如果需要修改系統的行爲,無須對抽象層進行任何改動,只需要增加新的具體類來實現新的業務功能即可,實現在不修改已有代碼的基礎上擴展系統的功能,達到開閉原則的要求。

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

里氏代換原則

Liskov Substitution Principle(LSP):子類可以擴展基類的功能,但是不能改變基類原有的功能。所有引用基類的地方必須能透明的使用其子類的對象。里氏替換原則克服繼承的缺點

子類可以實現基類的抽象方法,但不能覆蓋基類的非抽象方法
子類中可以增加自己特有的方法
當子類的方法重載基類的方法時,方法的前置條件(即方法的形參)要比基類方法的輸入參數更寬鬆
當子類的方法實現基類的抽象方法時,方法的後置條件(即方法的返回值)要比基類更嚴格

繼承優點:代碼共享,減少創建類的工作量,每個子類都擁有基類的方法和屬性;提高代碼的重用性;子類可以形似基類,但又異於基類;提高代碼的可擴展性,實現基類的方法就可以“爲所欲爲”了;提高產品或項目的開放性。

繼承缺點:繼承是侵入性的。只要繼承,就必須擁有基類的所有屬性和方法;降低代碼的靈活性。子類必須擁有基類的屬性和方法;增強了耦合性。當父類的常量、變量和方法被修改時,必需要考慮子類的修改,而且在缺乏規範的環境下,這種修改可能帶來非常糟糕的結果;大片的代碼需要重構。

里氏代換原則告訴我們,在軟件中將一個基類對象替換成它的子類對象,程序將不會產生任何錯誤和異常,反過來則不成立,如果一個軟件實體使用的是一個子類對象的話,那麼它不一定能夠使用基類對象。
里氏代換原則是實現開閉原則的重要方式之一,由於使用基類對象的地方都可以使用子類對象,因此在程序中儘量使用基類類型來對對象進行定義,而在運行時再確定其子類類型,用子類對象來替換父類對象。

儘量把基類設計成抽象類或接口,讓子類繼承基類或實現基類接口。增加一個新功能時,通過增加一個新的子類來實現。

依賴倒置原則

Dependence Inversion Principle(DIP):針對抽象編程,不要針對實現編程。是一個類與類之間的調用規則,依賴是指代碼中的耦合。代碼要依賴於抽象的類,而不要依賴於具體的類;要針對接口或抽象類編程,而不是針對具體類編程

依賴倒置原則的目的是通過要面向接口的編程來降低類間的耦合性
(1)每個類儘量提供接口或抽象類,或者兩者都具備。
(2)變量的聲明類型儘量是接口或者是抽象類。
(3)任何類都不應該從具體類派生。
(4)使用繼承時儘量遵循里氏替換原則

依賴倒置原則的作用:降低類間的耦合性、提高系統的穩定性、減少並行開發引起的風險、提高代碼的可讀性和可維護性

接口隔離原則

Interface Segregation Principle(ISP):恰當的劃分角色和接口(分離接口)從而實現解耦。客戶端不應該依賴它不需要的接口,類間的依賴關係應該建立在最小的接口上。

接口隔離原則是爲了約束接口、降低類對接口的依賴性,遵循接口隔離原則有以下 5 個優點。

(1)將臃腫龐大的接口分解爲多個粒度小的接口,可以預防外來變更的擴散,提高系統的靈活性和可維護性。
(2)接口隔離提高了系統的內聚性,減少了對外交互,降低了系統的耦合性。
(3)如果接口的粒度大小定義合理,能夠保證系統的穩定性;但是,如果定義過小,則會造成接口數量過多,使設計複雜化;如果定義太大,靈活性降低,無法提供定製服務,給整體項目帶來無法預料的風險。
(4)使用多個專門的接口還能夠體現對象的層次,因爲可以通過接口的繼承,實現對總接口的定義。
(5)能減少項目工程中的代碼冗餘。過大的大接口裏面通常放置許多不用的方法,當實現這個接口的時候,被迫設計冗餘的代碼。

在具體應用接口隔離原則時,應該根據以下幾個規則來衡量。

(1)接口儘量小,但是要有限度。一個接口只服務於一個子模塊或業務邏輯。
(2)爲依賴接口的類定製服務。只提供調用者需要的方法,屏蔽不需要的方法。
(3)瞭解環境,拒絕盲從。每個項目或產品都有選定的環境因素,環境不同,接口拆分的標準就不同深入瞭解業務邏輯。
(4)提高內聚,減少對外交互。使接口用最少的方法去完成最多的事情。

接口隔離原則和單一職責原則都是爲了提高類的內聚性、降低它們之間的耦合性,體現了封裝的思想。

但兩者是不同的:
(1)單一職責原則注重的是職責,而接口隔離原則注重的是對接口依賴的隔離。
(2)單一職責原則主要是約束類,它針對的是程序中的實現和細節;接口隔離原則主要約束接口,主要針對抽象和程序整體框架的構建。

合成複用原則

Composite Reuse Principle(CRP):又稱爲組合/聚合複用原則。儘量使用對象組合,而不是繼承來達到複用的目的。子類與其父類關係密切,父類的修改必然會對子類造成影響,而如果不小心對父類方法進行修改,就會造成子類錯誤的發生,這種依賴關係增加了類間的依賴關係,提高了耦合度。

在面向對象設計中,可以通過兩種基本方法在不同的環境中複用已有的設計和實現,即通過組合/聚合關係或通過繼承。
繼承複用:實現簡單,易於擴展。破壞系統的封裝性;從基類繼承而來的實現是靜態的,不可能在運行時發生改變,沒有足夠的靈活性;只能在有限的環境中使用。(“白箱”複用 )
組合/聚合複用:耦合度相對較低,選擇性地調用成員對象的操作;可以在運行時動態進行。(“黑箱”複用 )

組合/聚合可以使系統更加靈活,類與類之間的耦合度降低,一個類的變化對其他類造成的影響相對較少,因此一般首選使用組合/聚合來實現複用;其次才考慮繼承,在使用繼承時,需要嚴格遵循里氏代換原則,有效使用繼承會有助於對問題的理解,降低複雜度,而濫用繼承反而會增加系統構建和維護的難度以及系統的複雜度,因此需要慎重使用繼承複用。

迪米特原則

Law of Demeter(LoD):也叫最小知識原則。一個對象應該對其他對象有最少的瞭解,只和直接的朋友通信;類間解耦,弱耦合。簡單地說,迪米特法則就是指一個軟件實體應當儘可能少的與其他實體發生相互作用。這樣,當一個模塊修改時,就會盡量少的影響其他的模塊,擴展會相對容易,這是對軟件實體之間通信的限制,它要求限制軟件實體之間通信的寬度和深度。類間鬆耦合,類間耦合度越低,其複用性越強

迪米特法則可分爲狹義法則和廣義法則:

狹義的迪米特法則:可以降低類之間的耦合,但是會在系統中增加大量的小方法並散落在系統的各個角落,它可以使一個系統的局部設計簡化,因爲每一個局部都不會和遠距離的對象有直接的關聯,但是也會造成系統的不同模塊之間的通信效率降低,使得系統的不同模塊之間不容易協調。
廣義的迪米特法則:指對對象之間的信息流量、流向以及信息的影響的控制,主要是對信息隱藏的控制。信息的隱藏可以使各個子系統之間脫耦,從而允許它們獨立地被開發、優化、使用和修改,同時可以促進軟件的複用,由於每一個模塊都不依賴於其他模塊而存在,因此每一個模塊都可以獨立地在其他的地方使用。一個系統的規模越大,信息的隱藏就越重要,而信息隱藏的重要性也就越明顯。

在具體應用迪米特原則時,應該根據以下幾個規則來衡量。

(1)在類的劃分上,應當儘量創建鬆耦合的類,類之間的耦合度越低,就越有利於複用,一個處在鬆耦合中的類一旦被修改,不會對關聯的類造成太大波及;
(2)在類的結構設計上,每一個類都應當儘量降低其成員變量和成員函數的訪問權限;
(3)在類的設計上,只要有可能,一個類型應當設計成不變類;
(4)在對其他類的引用上,一個對象對其他對象的引用應當降到最低。

總結

七大原則是提高面向對象編程代碼質量的必備原則,另外還是理解設計模式的必備前提。只有滿足了這七大原則,才能設計出穩定的軟件架構,但它們只是原則,有時還是需要學會靈活應變,千萬不要生搬硬套,否則只會把簡單問題複雜化。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章