我們在應用開發中,一般要求盡量做到可維護性和可復用性
應用程序的復用可以提高應用程序的開發效率和質量,節約開發成本,恰當的復用還可以改善系統的可維護性。而在面向對象的設計里面,可維護性和復用都是以面向對象設計原則為基礎的,這些設計原則首頁都是復用原則,遵循這些設計原則可以有效的提高系統的復用性,同時提高系統的可維護性。面向對象設計原則和設計模式也是對系統進行合理重構的指導方針。
常用的面向對象設計原則包括7個,這些原則 并不是孤立存在的,他們相互依賴,相互補充。
設計原則:
單一職責原則 ????
類的職責要單一,不能將太多的職責放在一個類中開閉原則?????
<p>軟件實體對擴展是開放的,但對修改是關閉的,即在不修改一個軟件實體的基礎上去擴展其功能里氏代換原則????
<p>在軟件系統中,一個可以接受基類對象的地方必然可以接受一個子類對象依賴倒轉原則?????
要針對抽象層編程,而不要針對具體類編程接口隔離原則??
使用多個專門接口來取代一個統一的接口合成復用原則????
在系統中應該盡量多使用組合和聚合關聯關系,盡量少使用甚至不使用繼承關系迪米特法則???
一個軟件實體對其它實體的引用越少越好,或者說如果兩個類不必彼此直接通信,那么這兩個類就不應當發生直接的相互作用,而是通過引入一個第三者發生間接交互
一、單一職責原則:類的職責要單一,不能將太多的職責放在一個類中。(高內聚、低耦合)
定義:一個對象應該只包含單一的職責,并且該職責被完整的封裝在一個類中。
- 原則分析
- 一個類(或者大道模塊,小到方法)承擔的職責越多,它被復用的可能性越小,而且如果一個類承擔的職責過多,就相當于將這些職責耦合在一起,當其中一個職責變化時,可能會影響到其它的職責運作。
- 類的職責主要包括兩個方面:數據職責和行為職責,數據職責通過其屬性體現,而行為職責通過其方法體現。
- 單一職責原則是實現高內聚、低耦合的指導方針,在很多代碼重構手法中都能找到他的存在,它是最簡單但又是最難運用的原則,需要設計人員發現類的不同職責將其分離,而發現的類多重職責需要設計人員具有較強的分析設計能力和相關重構經驗。
- 優點
- 降低類的復雜性,類的職責清晰明確。比如數據職責和行為職責清晰明確
- 提高可讀性和維護性
- 變更引起的風險減低,變更時必不可少的,如果接口的單一職責做得好,一個接口修改,只對相應的類有影響,對其它接口無影響,這對系統的擴展性、維護性都有非常大的幫助。
注意:單一職責原則提出了一個編寫程序的標準,用職責或變化原因來衡量接口或類設計得是否合理,但是職責和變化原因都是沒有具體標準的,一個類到底負責哪些職責?這些職責怎么細化?細化后是否都要有一個接口或類?這些都需要從實際的情況考慮。因項目而異,因環境而異。
二、開閉原則:對擴展開放,對修改關閉(設計模式的核心原則)
定義:一個軟件實體(如類、模塊和函數)應該對擴展開放,對修改關閉。意思是,在一個系統或者模塊中,對擴展是開發的,對修改是關閉的,一個好的系統是在不修改源代碼的情況下,可以擴展你的功能。而實現開閉原則的關健就是抽象化
- 原則分析
- 當軟件實體因需求要變化時,盡量通過擴展已有的軟件實體,可以提供新的行為,以滿足對軟件的新的需求,而不是修改已有的代碼,使變化中軟件有一定的適應性和靈活性。已有軟件模塊,特別是最重要的抽象層模塊不能修改,這使得變化中的軟件系統有一定的穩定性和延續性。
- 實現開閉原則的關健是抽象化:在“開-閉”原則中,不允許修改的是抽象的類或者接口,允許擴展的是具體的實體類,抽象類和接口在“開-閉”原則中扮演著極其重要的角色。即要預見可能變化的需求,又要預見所有可能已知的擴展。所以在這抽象化是關健
- 可變性的封閉原則:找到系統的可變因素,將他封裝起來。這是對“開-閉”原則最好的實現,不要把你的可變因素放在多個類中,或者散落在程序的各個角落。你應該將可變因素封套起來。并且切忌不要把所有的可變因素封套在一起。最好的解決辦法是,分塊封套你的可變因素。避免超大類,超長類,超長方法的出現。給你程序增加藝術氣息,將程序藝術化是我們的目標!
三、里氏代換原則:任何基類可以出現的地方,子類也可以出現
定義:第一種定義方式相對嚴格:如果對每一個類型為S的對象O1,都有類型為T的對象O2,使得以T定義的所有程序P在所在的對象O1都代換成O2時,程序P的行為沒有變化,那么類型S是類型T的子類。
第二種容易理解的定義方式:所有引用基類(父類)的地方必須能透明的使用其子類的對象。即子類能夠替換基類出現的地方。子類也能夠在基類的基礎上新增行為。
原則分析
講的是基類和子類的關系,只有這種關系存在時,里式代換原則才存在。正方形是長方形是理解里式代換原則的經典例子
里氏代換原則可以通俗的表述為:在軟件中如果能夠使用基類對象,那么一定能夠使用子類對象。把基類都替換為子類,程序將不會產生任何錯誤和異常,反過來則不成立,如果一個軟件實體使用的是一個子類的話,那么它不一定能都使用基類。
里氏代換原則是實現開閉原則的重要方式之一,由于使用基類對象的地方都可以用子類對象,因此在程序中盡量使用基類類型來對對象進行定義,而在運行時再確定子類類型,用子類對象來替換父類對象。
優點
代碼共享,減少創建類的工作量,每個子類都擁有父類的方法和屬性
提高代碼的重用性
子類可以形似父類,但又異于父類
提高代碼的可擴展性,實現父類的方法就可以“為所欲為”了
提高產品或項目的開發性
缺點
繼承是侵入性的。只要繼承,就必須擁有父類的所有屬性和方法
降低代碼的靈活性。子類必須擁有父類的屬性和方法,讓子類自由的世界中多了些約束
增強了耦合性。當父類常量、變量和方法被修改,必須考慮子類的修改,而且在缺乏規范的環境下,這種修改可能帶來非常糟糕的結果,大片代碼需要重構
四、依賴倒轉原則:要依賴抽象,而不要依賴具體的實現
定義:高層模塊不應該依賴底層模塊,它們都應該依賴抽象。抽象不應該依賴于細節,細節應該依賴于抽象。依賴倒轉原則要求客戶端依賴于抽象耦合。
- 原則表述:
- 抽象不應當依賴于細節;細節應當依賴于抽象;
- 要針對接口編程,不針對實現編程。
- 原則分析
- 如果說開閉原則是面向對象設計的目標,依賴倒轉原則是到達面向設計開閉原則的手段,如果要達到最好的開閉原則,就要盡量遵守依賴倒轉原則,可以說依賴倒轉原則對抽象的最好規范! 其實,依賴倒轉原則也是里氏代換原則的補充,理解了里氏代換原則,再來理解依賴倒轉原則應該很容易的。
- 依賴倒轉原則的常用方式之一是在代碼中使用抽象類,二將具體類放在配置文件中。
- 類之間的耦合:零耦合關系,抽象耦合關系。依賴倒轉原則要求客戶端依賴于抽象耦合,一抽象方式耦合是依賴倒轉原則的關鍵
理解這個依賴倒置,首先我們需要明白依賴在面向對象設計的概念:
依賴關系:是一種使用關系,特定事務的改變有可能會影響到使用該事物的其他事物,在需要表示一個事物使用另一個事物時使用依賴關系。大多數情況系,依賴關系體現在某個類的方法使用另一個類的對象作為參數。
- 優點
采用依賴倒轉原則減少類間的耦合性,提高系統的穩定性,降低并行開發引起的風險,提高代碼的可讀性和可維護性。
依賴正置就是類間的依賴是實實在在的實現類間的依賴,也就是面向實現編程這也是正常人的思維方式,我要開奔馳車就依賴奔馳車,我要使用筆記本就直接依賴筆記本,而編寫程序需要的是對現實世界的事物進行抽象,抽象的結構就是有了抽象類和接口,然后我們根據系統設計的需求產生了抽象間的依賴,代替了人們傳統思維中的事物間的依賴,倒置就是從這里產生的。
五、合成/聚合復用原則:要盡量使用對象組合,而不是繼承關系達到軟件復用的目的
定義:經常又叫做合成復用原則,盡量使用對象組合而不是繼承來達到復用的目的。就是在一個新的對象里面使用一些已有的對象,使之成為新對象的一部分;新對象通過向這些對象的委派達到復用已有功能的目的。簡而言之,要盡量使用合成/聚合,盡量不要使用繼承
。
原則分析
1)在面向對象設計中,可以通過兩種基本方法在不同的環境中復用已有的設計和實現,即通過組合/聚合關系或通過繼承關系。
繼承復用:實現簡單,易于擴展。破話系統的封裝性;從基類繼承而來的實現是靜態的。不可能在運行時發生改變,沒有足夠的靈活性;只有在有限的環境中使用 白箱復用
組合/聚合復用:耦合度相對較低,選擇性的調用成員對象的操作;可以在運行時動態進行。黑箱復用
2)組合/聚合可以使系統更加靈活,類與類之間的耦合度降低,一個類的變化對其他類造成的影響相對較小,因此一般首先選擇使用組合/聚合來實現復用;其次才考慮繼承,在使用繼承時,需要嚴格遵循里氏代換原則。有效的使用繼承會有助于對問題的理解,較低復雜度。而亂用繼承反而會增加系統構建和維護的難度以及系統的復雜度,因此需要慎重使用繼承復用。
3)此原則與里氏代換原則相輔相成,兩者都是事先開-閉原則的規范。違反這一原則,就無法實現開-閉原則。-
首先我們要明白合成和聚合的概念:
- 什么是組合?
合成(組合):表示一個整體與部分的關系,指一個依托整體而存在的關系(整體與部分不可以分開),例如:一個人對他的房子和家具,其中他的房子和家具是不能被共享的,因為那些東西都是他自己的。并且人沒了,這個也關系就沒了。這個例子就好像,烏雞百鳳丸這個產品,它是有烏雞和上等藥材合成而來的一樣。也比如網絡游戲中的武器裝備合成一樣,多種東西合并為一種超強的東西一樣。
雖然組合表示的是一個整體與部分的關系,但是組合關系中部分和整體具有統一的生存期。一旦整體對象不存在,部分對象也將不存在,部分對象與整體對象之間具有同生共死的關系。
在組合關系中,成員類是整體類的一部分,而且整體類可以控制成員類的生命周期,即成員類的存在依賴于整體類。 - 什么是聚合?
聚合:聚合是比合成關系的一種更強的依賴關系,也表示整體與部分的關系(整體與部分可以分開),例如,一個奔馳S360汽車,對奔馳S360引擎,奔馳S360輪胎的關系..這些關系就是帶有聚合性質的。因為奔馳S360引擎和奔馳S360輪胎他們只能被奔馳S360汽車所用,離開了奔馳S360汽車,它們就失去了存在的意義。在我們的設計中,這樣的關系不應該頻繁出現.這樣會增大設計的耦合度。
在面向對象中的聚合:通常在定義一個整體類后,再去分析這個整體類的組成結構,從而找出一些成員類,該整體類和成員類之間就形成了聚合關系。在聚合關系中,成員類是整體類的一部分,即成員對象是整體對象的一部分,但是成員對象可以脫離整體對象獨立存在。
- 什么是組合?
明白了合成和聚合關系,再來理解合成/聚合原則應該就清楚了。要避免在系統設計中出現,一個類的繼承層次超過3次。如果這樣的話,可以考慮重構你的代碼,或者重新設計結構. 當然最好的辦法就是考慮使用合成/聚合原則。
六、迪米特法則:系統中的類,盡量不要與其他類互相作用,減少類之間的耦合度
定義:又叫最少知識原則(Least Knowledge Principle或簡寫為LKP)\幾種形式定義: (1) 不要和“陌生人”說話。英文定義為:Don't talk to strangers. ?(2) 只與你的直接朋友通信。英文定義為:Talk only to your immediatefriends. (3) 每一個軟件單位對其他的單位都只有最少的知識,而且局限于那些與本單位密切相關的軟件單位。 簡單地說,也就是,一個對象應當對其它對象有盡可能少的了解。一個類應該對自己需要耦合或調用的類知道得最少,你(被耦合或調用的類)的內部是如何復雜都和我沒關系,那是你的事情,我就知道你提供的public方法,我就調用這么多,其他的一概不關心。
- 原則分析:
1)朋友類:
在迪米特法則中,對于一個對象,其朋友包括以下幾類:
?(1) 當前對象本身(this);
?(2) 以參數形式傳入到當前對象方法中的對象;
?(3) 當前對象的成員對象;
?(4) 如果當前對象的成員對象是一個集合,那么集合中的元素也都是朋友;
?(5) 當前對象所創建的對象。
任何一個對象,如果滿足上面的條件之一,就是當前對象的“朋友”,否則就是“陌生人”。
2)狹義法則和廣義法則:
在狹義的迪米特法則中,如果兩個類之間不必彼此直接通信,那么這兩個類就不應當發生直接的相互作用,如果其中的一個類需要調用另一個類的某一個方法的話,可以通過第三者轉發這個調用。

狹義的迪米特法則:可以降低類之間的耦合,但是會在系統中增加大量的小方法并散落在系統的各個角落,它可以使一個系統的局部設計簡化,因為每一個局部都不會和遠距離的對象有直接的關聯,但是也會造成系統的不同模塊之間的通信效率降低,使得系統的不同模塊之間不容易協調。
廣義的迪米特法則:指對對象之間的信息流量、流向以及信息的影響的控制,主要是對信息隱藏的控制。信息的隱藏可以使各個子系統之間脫耦,從而允許它們獨立地被開發、優化、使用和修改,同時可以促進軟件的復用,由于每一個模塊都不依賴于其他模塊而存在,因此每一個模塊都可以獨立地在其他的地方使用。一個系統的規模越大,信息的隱藏就越重要,而信息隱藏的重要性也就越明顯。
3)迪米特法則的主要用途:在于控制信息的過載。
?在類的劃分上,應當盡量創建松耦合的類,類之間的耦合度越低,就越有利于復用,一個處在松耦合中的類一旦被修改,不會對關聯的類造成太大波及;
?在類的結構設計上,每一個類都應當盡量降低其成員變量和成員函數的訪問權限;
?在類的設計上,只要有可能,一個類型應當設計成不變類;
?在對其他類的引用上,一個對象對其他對象的引用應當降到最低。
例子:外觀模式
系統中的類,盡量不要與其他類互相作用,減少類之間的耦合度,因為在你的系統中,擴展的時候,你可能需要修改這些類,而類與類之間的關系,決定了修改的復雜度,相互作用越多,則修改難度就越大,反之,如果相互作用的越小,則修改起來的難度就越小..例如A類依賴B類,則B類依賴C類,當你在修改A類的時候,你要考慮B類是否會受到影響,而B類的影響是否又會影響到C類. 如果此時C類再依賴D類的話,呵呵,我想這樣的修改有的受了。
七、接口隔離原則:客戶端不應該依賴哪些它不需要的接口。(這個法則與迪米特法則是相通的)
定義:客戶端不應該依賴那些它不需要的接口。另一種定義方法:一旦一個接口太大,則需要將它分割成一些更細小的接口,使用該接口的客戶端僅需知道與之相關的方法即可。 注意,在該定義中的接口指的是所定義的方法。例如外面調用某個類的public方法。這個方法對外就是接口。
- 原則分析:
1)接口隔離原則是指使用多個專門的接口,而不使用單一的總接口。每一個接口應該承擔一種相對獨立的角色,不多不少,不干不該干的事,該干的事都要干。
? (1)一個接口就只代表一個角色,每個角色都有它特定的一個接口,此時這個原則可以叫做“角色隔離原則”。
? (2)接口僅僅提供客戶端需要的行為,即所需的方法,客戶端不需要的行為則隱藏起來,應當為客戶端提供盡可能小的單獨的接口,而不要提供大的總接口。
2)使用接口隔離原則拆分接口時,首先必須滿足單一職責原則,將一組相關的操作定義在一個接口中,且在滿足高內聚的前提下,接口中的方法越少越好。
3)可以在進行系統設計時采用定制服務的方式,即為不同的客戶端提供寬窄不同的接口,只提供用戶需要的行為,而隱藏用戶不需要的行為。
下圖展示了一個擁有多個客戶類的系統,在系統中定義了一個巨大的接口(胖接口)AbstractService來服務所有的客戶類。可以使用接口隔離原則對其進行重構。
迪米特法則是目的,而接口隔離法則是對迪米特法則的規范. 為了做到盡可能小的耦合性,我們需要使用接口來規范類,用接口來約束類.要達到迪米特法則的要求,最好就是實現接口隔離法則,實現接口隔離法則,你也就滿足了迪米特法則。
內容轉載
http://blog.csdn.net/husen1314/article/details/42060667
http://blog.csdn.net/hguisu/article/details/7571617