java設計模式:面向對象設計的7個原則

在軟件開發中,爲了提升軟件系統的可維護性和可複用性,增長軟件的可擴展性和靈活性,程序員要儘可能根據7條原則來開發程序,從而提升軟件開發效率,節約軟件開發成本和維護成本。程序員

這7條原則分別是:開閉原則里氏替換原則依賴倒置原則單一職責原則接口隔離原則迪米特法則合成複用原則編程

接下來是對這7個原則的詳細介紹。設計模式

開閉原則(OCP,Open Closed Principle)架構

定義框架

開閉原則(Open Closed Principle,OCP)由勃蘭特·梅耶(Bertrand Meyer)提出,他在 1988 年的著做《面向對象軟件構造》(Object Oriented Software Construction)中提出:軟件實體應當對擴展開放,對修改關閉(Software entities should be open for extension,but closed for modification),這就是開閉原則的經典定義。測試

當應用的需求發生改變時,在不修改軟件實體的源代碼或者二進制代碼的前提下,能夠擴展模塊的功能,使其知足新的需求。spa

這裏的軟件實體包含幾個部分:項目中劃分出的模塊、類和接口、方法。設計

做用對象

開閉原則是面向對象程序設計的終極目標,它使軟件實體擁有必定的適用性和靈活性的同時具有穩定性和延續性。繼承

對軟件測試的影響

軟件遵循開閉原則的話,軟件測試時只須要對擴展的代碼進行測試就能夠了,由於原有的測試代碼依然可以正常運行。

能夠提升代碼的可複用性

粒度越小,被服用的可能性就越大;在面向對象的程序設計中,根據原則和抽象編程能夠提升代碼的可複用性。

能夠提升軟件的可維護性

遵照開閉原則的軟件,其穩定性高和延續性強,從而易於擴展和維護。

實現方法

能夠經過【抽象約束、封裝變化】來實現開閉原則,即經過接口或抽象類爲軟件實體定義一個相對穩定的抽象層,而將相同的可變因素封裝在相同的具體實現類中。

由於抽象靈活性好,適應性廣,只要抽象得合理,能夠基本保持軟件架構的穩定,而軟件中易變的細節能夠從抽象派生來的實現類來進行擴展,當軟件須要發生變化的時候,只須要根據需求從新派生一個實現類來擴展就能夠了。

里氏替換原則(LSP,Liskov Subsititution Principle)

定義

里氏替換原則(Liskov Substitution Principle,LSP)由麻省理工學院計算機科學實驗室的里斯科夫(Liskov)女士在 1987 年的「面向對象技術的高峯會議」(OOPSLA)上發表的一篇文章《數據抽象和層次》(Data Abstraction and Hierarchy)裏提出來的,她提出:繼承必須確保超類所擁有的性質在子類中仍然成立(Inheritance should ensure that any property proved about supertype objects also holds for subtype objects)。

里氏替換原則主要闡述了有關繼承的一些原則,也就是何時應該使用繼承,何時不該該使用繼承,以及其中蘊含的原理。里氏替換原則是繼承複用的基礎,它反映了基類與子類之間的關係,是對開閉原則的補充,是對實現抽象化的具體步驟的規範。

做用

里氏替換原則是實現開閉原則的重要方式之一。

里氏替換原則克服了繼承中重寫父類形成的可複用性變差的缺點。

里氏替換原則是動做正確性的保證。即類的擴展不會給已有的系統引入新的錯誤,下降了代碼出錯的可能性。

實現方法

里氏替換原則通俗地來說就是:子類能夠擴展父類的功能,但不能改變父類原有的功能。也就是說,子類繼承父類時,除了添加新的方法完成新增功能外,儘可能不要重寫父類的方法。

若是經過重寫父類的方法來完成新的功能,這樣寫起來雖然簡單,可是整個繼承體系的可複用性會比較差,特別是運用多態比較頻繁時,程序運行出錯的機率會很是大。

若是程序違背了里氏替換原則,則繼承類的對象在基類出現的地方會出現運行錯誤。這時其修正方法是:取消原來的繼承關係,從新設計它們之間的關係。

關於里氏替換原則的例子,最有名的是【正方形不是長方形】。固然,生活中也有不少相似的例子,例如,企鵝、鴕鳥和幾維鳥從生物學的角度來劃分,它們屬於鳥類,可是從類的繼承關係來看,因爲它們不能繼承鳥類會飛的功能,因此它們不能定義成鳥類的子類。一樣的,因爲氣球魚不會游泳,也不可以被定義成會游泳的魚類的子類。

依賴倒置原則(DIP,Dependence Inversion Principle)

定義

依賴倒置原則(Dependence Inversion Principle,DIP)是 Object Mentor 公司總裁羅伯特·馬丁(Robert C.Martin)於 1996 年在 C++ Report 上發表的文章。

依賴倒置原則的原始定義爲:高層模塊不該該依賴低層模塊,二者都應該依賴其抽象;抽象不該該依賴細節,細節應該依賴抽象(High level modules shouldnot depend upon low level modules.Both should depend upon abstractions.Abstractions should not depend upon details. Details should depend upon abstractions)。其核心思想是:要面向接口編程,不要面向實現編程。

依賴倒置原則是實現開閉原則的重要途徑之一,它下降了客戶與實現模塊之間的耦合。

因爲在軟件設計中,細節具備多變性,而抽象層則相對穩定,所以以抽象爲基礎搭建起來的架構要比以細節爲基礎搭建起來的架構要穩定得多。這裏的抽象指的是接口或抽象類,而細節則是指具體的實現類。

使用接口或抽象類的目的是制定好規範和契約,而不去涉及任何具體的操做,把展示細節的任務交給它們的實現類去完成。

做用

依賴倒置原則能夠下降類間的耦合性。

依賴倒置原則能夠提升系統的穩定性。

依賴倒置原則能夠減小並行開發引發的風險。

依賴倒置原則能夠提升代碼的可讀性和可維護性。

實現方法

依賴倒置原則的目的是經過要面向接口的編程來下降類間的耦合性,因此咱們在實際編程中只要遵行如下4點,就能在項目中知足這個規則。

每一個類儘可能提供接口或抽象類,或者二者都具有。

變量的聲明類型儘可能是接口或抽象類。

任何類都不該該從具體類派生。

使用繼承時儘可能遵循里氏替換原則。

單一職責原則(SRP,Single Responsibility Principle)

定義

單一職責原則(Single Responsibility Principle,SRP)又稱單一功能原則,由羅伯特·C.馬丁(Robert C. Martin)於《敏捷軟件開發:原則、模式和實踐》一書中提出的。這裏的職責是指類變化的緣由,單一職責原則規定一個類應該有且僅有一個引發它變化的緣由,不然類應該被拆分(There should never be more than one reason for a class to change)。

該原則提出,對象不該該承擔太多職責,若是一個對象承擔了太多的職責,至少存在如下兩個缺點:

一個職責的變化可能會削弱或者抑制這個類實現其餘職責的能力;

當客戶端須要該對象的某一職責時,不得不將其餘不須要的職責全都包含進來,從而形成榮冗餘代碼或代碼的浪費。

優勢

單一職責的核心就是控制類的粒度大小,將對象解耦,提升其內聚性。若是遵循單一職責原則將會由如下優勢:

下降類的可讀性

一個類只負責一項職責,其邏輯確定要比負責多項職責簡單得多。

提升類的可讀性

複雜性下降,天然其可讀性會提升。

提升系統的可維護性

可讀性提升,天然是更容易維護了。

下降變動引發的風險

變動幾乎是必然的,若是單一職責原則遵照得好,當修改一個功能時,能夠顯著下降對其餘功能得影響。

實現方法

單一職責原則是最簡單但又是最難運用的原則,須要設計人員發現類的不一樣職責並將其分離,再封裝到不一樣的類或模塊中。而發現類的多重職責須要設計人員具備較強的分析設計能力和相關重構經驗。

單一職責原則一樣也適用於方法。一個方法應該儘量作好一件事情。若是一個方法處理的事情太多,其顆粒度會變得很粗,不利於重用。

接口隔離原則(ISP,Interface Segregation Principle)

定義

接口隔離原則(Interface Segregation Principle,ISP)要求程序員儘可能將臃腫龐大的接口拆分紅更小的和更具體的接口,讓接口中只包含客戶感興趣的方法。

2002 年羅伯特·C.馬丁給「接口隔離原則」的定義是:客戶端不該該被迫依賴於它不使用的方法(Clients should not be forced to depend on methods they do not use)。該原則還有另一個定義:一個類對另外一個類的依賴應該創建在最小的接口上(The dependency of one class to another one should depend on the smallest possible interface)。

以上兩個定義的含義是:要爲各個類創建它們須要的專用接口,而不要試圖去創建一個很龐大的接口供全部依賴它的類去調用。

接口隔離原則和單一職責原則都是爲了提升類的內聚性,下降它們之間的耦合性,體現了封裝的思想,但二者是不一樣的,不一樣在於:

單一職責原則注重的是職責,而接口隔離原則注重的是對接口依賴的隔離;

單一職責原則主要是約束類,它針對的是程序中的實現和細節;接口隔離原則主要是約束接口,針對抽象和程序總體框架的構建。

優勢

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

將臃腫龐大的接口分解爲多個粒度小的接口,能夠預防外來變動的擴散,提升系統的靈活性和可維護性。

提升了系統的內聚性,減小了對外交互,下降了系統的耦合性。

若是接口的粒度大小定義合理,可以保證系統的穩定性;可是,若是定義太小,則會形成接口數量過多,使設計複雜化;若是定義太大,靈活性下降,沒法提供定製服務,給總體項目帶來沒法預料的風險。

使用多個專門的接口可以體現對象的層次,由於能夠經過接口的繼承實現對總接口的定義。

能減小項目工程中的代碼冗餘。過大的大接口裏面一般會防止許多用不到的方法,當實現這個接口的時候,會被迫去添加冗餘的代碼。

實現方法

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

接口儘可能小,可是要有限度。一個接口只服務於一個子模塊或業務邏輯。

爲依賴接口的類定製服務。只提供調用者須要的方法,屏蔽不須要的方法。

瞭解環境,拒絕盲從。每一個項目或產品都有選定的環境因素。環境不一樣,接口拆分的標準就不一樣。拆分接口的時候要深刻了解業務邏輯。

提升內聚,減小對外交互。使接口用最少的方法去完成最多的事情。

迪米特法則(LoD,Law of Demeter)

定義

迪米特法則(Law of Demeter,LoD)又叫做最少知識原則(Least Knowledge Principle,LKP),產生於 1987 年美國東北大學(Northeastern University)的一個名爲迪米特(Demeter)的研究項目,由伊恩·荷蘭(Ian Holland)提出,被 UML 創始者之一的布奇(Booch)普及,後來又由於在經典著做《程序員修煉之道》(The Pragmatic Programmer)說起而廣爲人知。

迪米特法則的定義是:只與你的直接朋友交談,不跟「陌生人」說話(Talk only to your immediate friends and not to strangers)。其含義是:若是兩個軟件實體無須直接通訊,那麼就不該當發生直接的相互調用,能夠經過第三方轉發該調用。其目的是下降類之間的耦合度,提升模塊的相對獨立性。

迪米特法則中的【朋友】是指:當前對象自己、當前對象的成員對象、當前對象所建立的對象、當前對象的方法參數等。這些對象同當前對象存在關聯、聚合或組合關係,能夠直接訪問這些對象的方法。

優勢

迪米特法則要求限制軟件實體之間通訊的寬度和深度,正確使用迪米特法則,有兩個優勢:

下降類之間的耦合度,提升模塊的相對獨立性。

因爲耦合度下降,從而提升類的可複用率和系統的擴展性。

可是,過分使用迪米特法則,會使系統產生大量的中介類,致使系統的複雜度增長和模塊之間的通訊效率下降。因此在使用迪米特法則的時候每每須要反覆權衡,以確保在高內聚低耦合的同時,可以保證系統的結構清晰。

實現方法

從迪米特法則的定義和特色可知,它強調如下2點:

從依賴者的角度來講,只依賴應該依賴的對象。

從被依賴者的角度說,只暴露應該暴露的方法。

因此,在運用迪米特法則時要注意如下6點:

在類的劃分上,應該建立弱耦合的類。類與類之間的耦合越弱,就越有利於實現可複用的目標。

在類的結構設計上,儘可能下降類成員的訪問權限。

在類的設計上,優先考慮將一個類設置成不變類。

在對其餘類的引用上,將引用其餘對象的次數降到最低。

謹慎使用序列化(Serializable)功能。

合成複用原則(CRP,Composite Reuse Principle)

定義

合成複用原則(Composite Reuse Principle,CRP)又叫組合/聚合複用原則(Composition/Aggregate Reuse Principle,CARP)。它要求在軟件複用時,要儘可能先使用組合或者聚合等關聯關係來實現,其次才考慮使用繼承關係來實現。

若是要使用繼承關係,則必須嚴格遵循里氏替換原則。合成複用原則是和里氏替換原則相輔相成的,二者都是開閉原則的具體實現規範。

優勢

一般類的複用分爲繼承複用和合成複用兩種,繼承複用雖然有簡單和易實現的優勢,可是它也存在如下缺點:

繼承複用破壞了類的封裝性。由於繼承會將父類的實現細節暴露給子類,父類對子類是透明的,因此這種複用又被稱爲【白箱複用】。

子類與父類的耦合度高。父類的實現的任何改變都會致使子類的實現發生變化,這不利於類的擴展與維護。

限制了複用的靈活性。從父類繼承而來的實現是靜態的,在編譯時已經定義,因此在運行時不可能發生變化。

採用組合或聚合複用時,能夠將已有對象歸入新對象中,使之成爲新對象的一部分,新對象能夠調用已有對象的功能,具備如下優勢:

維持類的封裝性。由於成分對象的內部細節是新對象看不見的,多以這種複用又被稱爲【黑箱複用】。

新舊類之間的耦合度低。這種複用所須要的依賴較少,新對象存取成分對象的惟一方法是經過成分對象的接口。

複用的靈活性高。這種複用能夠在運行時動態進行,新對象能夠動態地引用與成分對象類型相同的對象。

實現方法

合成複用原則是經過將已有的對象歸入新對象中,做爲新對象的成員對象來實現的,新對象能夠調用已有對象的功能,從而達到複用的目的。

 

上述的7個設計原則,是軟件設計模式必須遵循的原則。各個原則要求的側重點不一樣。其中,開閉原則是總綱,它告訴咱們要對擴展開放,對修改關閉;里氏替換原則告訴咱們不要破壞繼承體系;依賴倒置原則告訴咱們要面向接口編程;單一職責原則告訴咱們實現類要職責單一;接口隔離原則告訴咱們在設計接口的時候要精簡單一;迪米特法則告訴咱們要下降耦合度;合成複合原則告訴咱們要優先使用組合或者聚合關係複用,少用繼承關係複用。

相關文章
相關標籤/搜索