天天看點

軟體設計六大原則軟體設計七大原則

軟體設計七大原則

所謂的軟體設計原則,就是開發人員在長久的軟體開發過程中整理總結的約定俗成的設計原則。遵循這些原則就能夠對軟體的開發和維護起到很好的作用。說白了就是程式員在編寫代碼時高瞻遠矚的設計思想。其中這種思想應用較多的就是設計模式。

七大原則分别為:

1.開閉原則

2.單一職責原則

3.依賴倒置原則

4.接口隔離原則

5.迪米特法則(最少知道原則)

6.裡氏替換原則

7.合成複用原則

其中第七條合成複用原則有的文章裡把這個去掉了變成了六大設計原則

一、開閉原則

開閉原則,Software entities like classes, modules and functions should be open for extension but closed for modifications,軟體實體比如類,子產品,和函數應該對拓展開放,對修改關閉,也就是盡量通過拓展實體行為來實作變化。

特點:

  • 通過接口或者抽象類限制擴充,對擴充進行邊界限定,不允許出現在接口或抽象類中不存在的public方法
  • 參數類型、引用對象盡量使用接口或者抽象類,而不是實作類
  • 抽象層盡量保持穩定,一旦确定即不允許修改

開閉原則是面向對象設計中最基礎的設計原則,它指導我們如何建立穩定靈活的系統,開閉原則隻定義了對修改關閉,對擴充開放。其實隻要遵循後面5種設計模式,設計出來的軟體就是符合開閉原則的。回想起實際開發時,一個版本上線,下一次開發時需要增加新的功能,在接口裡新添一個方法,導緻所有實作了該接口的類都要修改,甚至其他引用了該類所在jar包的項目都要修改,而這個項目甚至有可能不是你所在小組負責的。那麼這種設計就是不符合開閉原則的。

二、單一職責原則

單一職責原則的定義是:“There should never be more than one reason for a class to change.”,也就是有且僅有一個原因引起類的變更。這樣可以降低類的複雜性,實作什麼職責都有清晰明确的定義;可讀性提高;可維護性提高;變更引起的風險降低。

這個應該比較好了解,一個類如果負責的功能太多,那麼代碼的耦合度可能會大幅度提升,不僅給代碼可讀性帶來了巨大的難度,代碼的維護性上也大幅度降低。想一想,當你接到一個任務去修改這個類或者與這個類有關的類的時候,發現由此而引發的“雪崩”效應,牽一發而動全身,引出了多個類需要修改,而這些類可能與你要實作的功能毫不相關,恰巧開發這個類的人已經離職了,那麼可想而知會有多麼頭疼了。

三、依賴倒置原則

依賴倒置原則,子產品間的依賴通過抽象發生,實作類之間不發生直接的依賴關系,其依賴關系是通過接口或抽象類産生的;

接口或者抽象類不依賴于實作類;

實作類依賴與接口或抽象類。

需要每個類盡量都有接口或者抽象類;

變量的表面類型盡量是接口或者抽象類;

任何類都不應該從具體類派生(超過兩層), 盡量不要複寫基類的方法。

采用依賴倒置原則可減少類間的耦合性,提高系統的穩定性,降低并行開發引起的風險,提高代碼可讀性和可維護性。依賴也可以稱為注入,一般可以通過構造函數注入,屬性注入,接口注入。

程式要依賴于抽象接口,不要依賴于具體實作。簡單的說就是要求對抽象進行程式設計,不要對實作進行程式設計,這樣就降低了客戶與實作子產品間的耦合。高層子產品不應該依賴低層子產品。兩個都應該依賴抽象。

抽象不應該依賴細節。細節應該依賴抽象。

我曾給朋友舉個這麼個例子,在一個寝室中,很多人懶得去打飯。

是以一旦有一個人先說“我去食堂吃飯了!”那麼其餘的人一定會異口同聲的說“給我也帶一份”。

去食堂打飯的人會說,“好,那吃什麼由我來決定!”

這個同學去食堂打飯選擇了自己喜歡吃的炸雞,有可能給其他幾個同學也買了炸雞,也有可能看到其他視窗排隊的人少也給他們買了不一樣的菜。

回到設計原則來說,去食堂打飯的同學就是抽象,買什麼菜由這個同學決定而不是其他的人。其餘的人每個人之間也沒有聯系,他們全都依賴這個去食堂打飯的人。這樣,本身應該自己去買飯,變成了所有人依賴一個人去買飯,這樣就依賴反轉了。

記得剛學springIOC的時候,一直在講依賴注入和控制反轉。當時隻是死記硬背這麼個特點。當在後面接觸了才明白,我們在一個類裡面如果需要引入另一個對象 正常應該 new一個新對象,并指派。但是這樣做就産生了依賴。有了spring以後,spring就接管了這個創造對象的過程,依賴就變成了所有類依賴spring。具體如何接管實作請看相關文章。

四、接口隔離原則

接口隔離原則的定義是接口盡量細化,同時接口中的方法盡量少。需要一個接口隻服務于一個子子產品或者業務邏輯;通過業務邏輯盡量壓縮接口中的方法;這樣可以提高内聚,降低耦合,重用代碼。

用戶端不應該依賴它不需要的接口,類間的依賴關系應該建立在最小的接口上

五、迪米特法則(最少知道原則)

迪米特法則,也稱為最少知識原則,也就是一個對象應該對其他對象有最少的了解。需要隻和朋友交流;朋友間也是有距離的;自己的就是自己的;類間解耦,弱耦合。

這個面向對象開發中封裝思想的展現,對應到自然界,就是一個個獨立的個體,哪怕和其他人有了交集,但是這種交集也是很微弱的,不至于一個人受傷,多個人遭罪。在開發中也能降低耦合,這有點和單一職責有點像,但不完全一緻。單一職責原則是說一個類負責一個職責,但是迪米特法則是說對象之間的關系。

六、裡氏替換原則

裡氏代換原則由2008年圖靈獎得主、美國第一位計算機科學女博士Barbara Liskov教授和卡内基·梅隆大學Jeannette Wing教授于1994年提出。其嚴格表述如下:如果對每一個類型為S的對象o1,都有類型為T的對象o2,使得以T定義的所有程式P在所有的對象o1代換o2時,程式P的行為沒有變化,那麼類型S是類型T的子類型。這個定義比較拗口且難以了解,是以我們一般使用它的另一個通俗版定義:

裡氏代換原則(Liskov Substitution Principle, LSP):所有引用基類(父類)的地方必須能透明地使用其子類的對象。

 裡氏代換原則告訴我們,在軟體中将一個基類對象替換成它的子類對象,程式将不會産生任何錯誤和異常,反過來則不成立,如果一個軟體實體使用的是一個子類對象的話,那麼它不一定能夠使用基類對象。

      例如有兩個類,一個類為BaseClass,另一個是SubClass類,并且SubClass類是BaseClass類的子類,那麼一個方法如果可以接受一個BaseClass類型的基類對象base的話,如:method1(base),那麼它必然可以接受一個BaseClass類型的子類對象sub,method1(sub)能夠正常運作。反過來的代換不成立,如一個方法method2接受BaseClass類型的子類對象sub為參數:method2(sub),那麼一般而言不可以有method2(base),除非是重載方法。

      裡氏代換原則是實作開閉原則的重要方式之一,由于使用基類對象的地方都可以使用子類對象,是以在程式中盡量使用基類類型來對對象進行定義,而在運作時再确定其子類類型,用子類對象來替換父類對象。

介紹的比較好的文章:

https://www.cnblogs.com/vaiyanzi/p/6899402.html

https://www.cnblogs.com/chenxkang/p/6657384.html

原則:子類可以擴充父類的功能,但不能改變父類原有的功能。
     父類能出現的地方都可以用子類來代替,而且換成子類也不會出現任何錯誤或異常,而使用者也無需知道是父類還是子類,
     但反過來則不成立。總之,就是抽象。

     1. 子類必須完全實作父類的抽象方法,但不能覆寫父類的非抽象方法;
     2. 子類中可以增加自己特有的方法;
     3. 當子類的方法重載父類的方法時,方法的前置條件(即方法的形參)要比父類方法的輸入參數要更寬松;
     4.當子類的方法實作父類的抽象方法時,方法的後置條件(即方法的傳回值)要比父類更嚴格。

優點:
     1. 提高代碼的重用性,子類擁有父類的方法和屬性;
     2. 提高代碼的可擴充性,子類可形似于父類,但異于父類,保留自我的特性;

缺點:
     1. 繼承是侵入性的,隻要繼承就必須擁有父類的所有方法和屬性,在一定程度上限制了子類,降低了代碼的靈活性;
     2. 增加了耦合,當父類的常量、變量或者方法被修改了,需要考慮子類的修改,是以一旦父類有了變動,很可能會造成
        非常糟糕的結果,要重構大量的代碼。      

裡氏替換原則是開閉原則的具體實作手段之一。這也就是我們應該更多的依賴抽象,盡量少的依賴實作細節, 其實就是我們之前講的依賴倒置原則(DIP)。

七、合成複用原則

經常又叫做合成複用原則。合成/聚合複用原則就是在一個新的對象裡面使用一些已有的對象,使之成為新對象的一部分;新的對象通過向這些對象的委派達到複用已有功能的目的。它的設計原則是:要盡量使用合成/聚合,盡量不要使用繼承。

繼承的問題:

通過繼承來進行複用的主要問題在于繼承複用會破壞系統的封裝性,因為繼承會将基類的實作細節暴露給子類,由于基類的内部細節通常對子類來說是可見的,是以這種複用又稱“白箱”複用,如果基類發生改變,那麼子類的實作也不得不發生改變;從基類繼承而來的實作是靜态的,不可能在運作時發生改變,沒有足夠的靈活性;而且繼承隻能在有限的環境中使用(如類沒有聲明為不能被繼承)。

在面向對象設計中,我們可以通過兩種方法在不同的環境中複用已有的設計和實作,即通過組合/聚合關系或通過繼承,但首先應該考慮使用組合/聚合,組合/聚合可以使系統更加靈活,降低類與類之間的耦合度,一個類的變化對其他類造成的影響相對較少;其次才考慮繼承,在使用繼承時,需要嚴格遵循裡氏代換原則,有效使用繼承會有助于對問題的了解,降低複雜度,而濫用繼承反而會增加系統建構和維護的難度以及系統的複雜度,是以需要慎重使用繼承複用。

有人會講合成複用不會增加類之間的耦合度而且違背了單一職責原則嗎?但實際上我們開發不可能做到完全的解耦和單一職責。

在這種情況,如果想通過繼承的方式引入其他類可能耦合度會更高而靈活性會降低。

參考文章

https://www.cnblogs.com/Johar/p/9028240.html 軟體架構的六大設計原則

https://blog.csdn.net/liuzihaoboy/article/details/80830476 面向對象設計原則(六)合成複用原則