1. 程式人生 > 其它 >設計模式的6大原則

設計模式的6大原則

引用網址:https://zhuanlan.zhihu.com/p/105681983?from_voters_page=true

1.單一職責

一個類只負責一個功能領域中的相應職責,或者可以定義為:就一個類而言,應該只有一個引起它變化的原因。 

單一職責原則是實現高內聚、低耦合的指導方針,它是最簡單但又最難運用的原則。

如果一個類承擔的職責過多,就等於把這些職責耦合在一起了。一個職責的變化可能會削弱或者抑制這個類完成其他職責的能力。就比如介面和資料,要拆開。

解決方案:

將不同的職責封裝到不同的類或模組中。一個類只負責一項職責。

2.開閉原則(Open Close Principle)

一個軟體實體應當對擴充套件開放,對修改關閉。即軟體實體應儘量在不修改原有程式碼的情況下進行擴充套件。 

開閉原則是一個非常虛的原則,其餘5個原則是對開閉原則的具體解釋。

就像“好好學習,天天向上”的口號一樣,告訴我們要好好學習,但是學什麼,怎麼學並沒有告訴我們,需要去體會和掌握,開閉原則也是一個口號,那我們怎麼把這個口號應用到實際工作中呢?

解決方案:

  • 使用介面和抽象類,為系統定義一個相對穩定的抽象層,而將不同的實現行為移至具體的實現層中完成;
  • 引數型別、引用物件儘量使用介面或者抽象類,而不是實現類;
  • 抽象層儘量保持穩定,一旦確定即不允許修改;

為了滿足開閉原則,需要對系統進行抽象化設計,抽象化是開閉原則的關鍵。在Java、C#等程式語言中,可以為系統定義一個相對穩定的抽象層,而將不同的實現行為移至具體的實現層中完成。想要達到這樣的效果,我們需要使用介面和抽象類,可以通過它們定義系統的抽象層,再通過具體類來進行擴充套件。如果需要修改系統的行為,無須對抽象層進行任何改動,只需要增加新的具體類來實現新的業務功能即可,實現在不修改已有程式碼的基礎上擴充套件系統的功能,達到開閉原則的要求。

3.里氏代換原則(Liskov Substitution Principle)

任何基類可以出現的地方,子類一定可以出現。

LSP 是繼承複用的基石,只有當派生類可以替換掉基類,且軟體單位的功能不受到影響時,基類才能真正被複用,而派生類也能夠在基類的基礎上增加新的行為。里氏代換原則是對開閉原則的補充。實現開閉原則的關鍵步驟就是抽象化,而基類與子類的繼承關係就是抽象化的具體實現,所以里氏代換原則是對實現抽象化的具體步驟的規範。

遵循里氏替換原則時,子類儘量不去重寫基類的方法,如果需要大量重寫時,建議再創造一個基類的基類,並繼承之,使得其和之前的基類是兄弟關係,而不是父子關係;

4.依賴倒轉原則(Dependence Inversion Principle)

抽象(介面/抽象類)不應該依賴於細節,細節應該依賴於抽象(介面/抽象類)。
高層模組不應該依賴於低層模組,兩者都應該依賴其抽象。

這個原則是開閉原則的基礎,具體內容:

抽象指的是介面或者抽象類。細節指的是具體實現類。

中心思想是面向介面程式設計;針對介面程式設計,依賴於抽象而不依賴於具體。

比如有介面ICar,子類有Car1和Car2.我們在處理邏輯的時候,直接呼叫ICar的方法,而不是在程式碼裡呼叫Car1或者Car2.

如何在專案中使用這個規則呢?

  • 每個類儘量都有介面或者抽象類,或者都有
  • 變數的表面型別儘量是介面或者抽象類

5.介面隔離原則(Interface Segregation Principle)

客戶端不應該依賴它不需要的介面
類間的依賴關係應該建立在最小的介面上

其實通俗來理解就是,客戶端在呼叫方法時,應當依賴於某個需要的介面,而不是一個很大很廣泛的介面或者類。這就要求我們不要在一個接口裡面放很多的方法,這樣會顯得這個類很臃腫不堪。所以介面應該儘量拆分,一個介面對應一種功能。

或許看到介面隔離原則這樣的定義很多人會覺得和單一職責原則很像,但是這兩個原則還是有著很鮮明的區別。介面隔離原則和單一職責原則的審視角度是不同的,單一職責原則要求類和介面職責單一,注重的是職責,是業務邏輯上的劃分,而介面隔離原則要求方法要儘可能的少,是在介面設計上的考慮。例如一個介面的職責包含10個方法,這10個方法都放在一個介面中,並且提供給多個模組訪問,各個模組按照規定的許可權來訪問,並規定了“不使用的方法不能訪問”,這樣的設計是不符合介面隔離原則的,介面隔離原則要求“儘量使用多個專門的介面”,這裡專門的介面就是指提供給每個模組的都應該是單一介面(即每一個模組對應一個介面),而不是建立一個龐大臃腫的介面來容納所有的客戶端訪問。

在專案中,我們會經常這樣做:

類A:
{
    類B b;
}

類B:
{
    Fun1();
    Fun2();
}

一個類A,即使只需要類B的某些功能時,還是會持有一個類B的例項。這樣相當於把整個類B的所有功能都暴露給了外部,這樣是不好的,我們可以修改為類A只持有需要功能的介面例項。因此需要將類B的所有功能拆分為N個介面,繼承介面並實現方法。

介面隔離原則是對介面進行規範約束,其包含以下4層含義:

  • 介面要儘量小:這是核心定義,不出現臃腫的介面,要儘量拆分為n個小介面。但是首先要滿足單一職責。
  • 介面要高內聚:提高介面,類,模組的處理能力,較少對外的互動。
  • 定製服務:單獨為一個個體提供優良的服務,只提供訪問者需要的方法。
  • 介面的設計是有限度的:介面的設計粒度越小,系統越靈活。但是靈活的同時,也帶來了結構的複雜化。那麼這個度如何判斷呢?沒有標準,看開發者的經驗和常識了。

總結就是:使用多個隔離的介面,比使用單個功能廣泛的介面要好。

6.迪米特法則,又稱最少知道原則(Demeter Principle)

一個物件應該對其他物件有最少的瞭解

一個實體應當儘量少地與其他實體之間發生相互作用,使得系統功能模組相對獨立。

迪米特法則對類的低耦合提出的明確的要求,包含了以下含義:

  • 只和朋友交流:朋友類的定義是這樣的:出現在成員變數、方法的輸入輸出引數中的類稱為成員朋友類,而出現在方法體內部的類不屬於朋友類。
  • 朋友也是有距離的:迪米特法則要求類“羞澀”一點,儘量不要對外公佈太多的public方法和非靜態的public變數,儘量內斂,多使用private、package-private、protected等訪問許可權。
  • 是你自己的就是你自己的:在實際應用中經常會出現這樣一個方法:放在本類中也可以,放在其他類中也沒有錯,那怎麼去衡量呢?正確做法:如果一個方法放在本類中,既不增加類間關係,也對本類不產生負面影響,那就放置在本類中。

迪米特法則的核心觀念就是類間解耦,弱耦合,只有弱耦合了以後,類的複用率才可以提高。

1.單一職責

一個類只負責一個功能領域中的相應職責,或者可以定義為:就一個類而言,應該只有一個引起它變化的原因。 

單一職責原則是實現高內聚、低耦合的指導方針,它是最簡單但又最難運用的原則。

如果一個類承擔的職責過多,就等於把這些職責耦合在一起了。一個職責的變化可能會削弱或者抑制這個類完成其他職責的能力。就比如介面和資料,要拆開。

解決方案:

將不同的職責封裝到不同的類或模組中。一個類只負責一項職責。

2.開閉原則(Open Close Principle)

一個軟體實體應當對擴充套件開放,對修改關閉。即軟體實體應儘量在不修改原有程式碼的情況下進行擴充套件。 

開閉原則是一個非常虛的原則,其餘5個原則是對開閉原則的具體解釋。

就像“好好學習,天天向上”的口號一樣,告訴我們要好好學習,但是學什麼,怎麼學並沒有告訴我們,需要去體會和掌握,開閉原則也是一個口號,那我們怎麼把這個口號應用到實際工作中呢?

解決方案:

  • 使用介面和抽象類,為系統定義一個相對穩定的抽象層,而將不同的實現行為移至具體的實現層中完成;
  • 引數型別、引用物件儘量使用介面或者抽象類,而不是實現類;
  • 抽象層儘量保持穩定,一旦確定即不允許修改;

為了滿足開閉原則,需要對系統進行抽象化設計,抽象化是開閉原則的關鍵。在Java、C#等程式語言中,可以為系統定義一個相對穩定的抽象層,而將不同的實現行為移至具體的實現層中完成。想要達到這樣的效果,我們需要使用介面和抽象類,可以通過它們定義系統的抽象層,再通過具體類來進行擴充套件。如果需要修改系統的行為,無須對抽象層進行任何改動,只需要增加新的具體類來實現新的業務功能即可,實現在不修改已有程式碼的基礎上擴充套件系統的功能,達到開閉原則的要求。

3.里氏代換原則(Liskov Substitution Principle)

任何基類可以出現的地方,子類一定可以出現。

LSP 是繼承複用的基石,只有當派生類可以替換掉基類,且軟體單位的功能不受到影響時,基類才能真正被複用,而派生類也能夠在基類的基礎上增加新的行為。里氏代換原則是對開閉原則的補充。實現開閉原則的關鍵步驟就是抽象化,而基類與子類的繼承關係就是抽象化的具體實現,所以里氏代換原則是對實現抽象化的具體步驟的規範。

遵循里氏替換原則時,子類儘量不去重寫基類的方法,如果需要大量重寫時,建議再創造一個基類的基類,並繼承之,使得其和之前的基類是兄弟關係,而不是父子關係;

4.依賴倒轉原則(Dependence Inversion Principle)

抽象(介面/抽象類)不應該依賴於細節,細節應該依賴於抽象(介面/抽象類)。
高層模組不應該依賴於低層模組,兩者都應該依賴其抽象。

這個原則是開閉原則的基礎,具體內容:

抽象指的是介面或者抽象類。細節指的是具體實現類。

中心思想是面向介面程式設計;針對介面程式設計,依賴於抽象而不依賴於具體。

比如有介面ICar,子類有Car1和Car2.我們在處理邏輯的時候,直接呼叫ICar的方法,而不是在程式碼裡呼叫Car1或者Car2.

如何在專案中使用這個規則呢?

  • 每個類儘量都有介面或者抽象類,或者都有
  • 變數的表面型別儘量是介面或者抽象類

5.介面隔離原則(Interface Segregation Principle)

客戶端不應該依賴它不需要的介面
類間的依賴關係應該建立在最小的介面上

其實通俗來理解就是,客戶端在呼叫方法時,應當依賴於某個需要的介面,而不是一個很大很廣泛的介面或者類。這就要求我們不要在一個接口裡面放很多的方法,這樣會顯得這個類很臃腫不堪。所以介面應該儘量拆分,一個介面對應一種功能。

或許看到介面隔離原則這樣的定義很多人會覺得和單一職責原則很像,但是這兩個原則還是有著很鮮明的區別。介面隔離原則和單一職責原則的審視角度是不同的,單一職責原則要求類和介面職責單一,注重的是職責,是業務邏輯上的劃分,而介面隔離原則要求方法要儘可能的少,是在介面設計上的考慮。例如一個介面的職責包含10個方法,這10個方法都放在一個介面中,並且提供給多個模組訪問,各個模組按照規定的許可權來訪問,並規定了“不使用的方法不能訪問”,這樣的設計是不符合介面隔離原則的,介面隔離原則要求“儘量使用多個專門的介面”,這裡專門的介面就是指提供給每個模組的都應該是單一介面(即每一個模組對應一個介面),而不是建立一個龐大臃腫的介面來容納所有的客戶端訪問。

在專案中,我們會經常這樣做:

類A:
{
    類B b;
}

類B:
{
    Fun1();
    Fun2();
}

一個類A,即使只需要類B的某些功能時,還是會持有一個類B的例項。這樣相當於把整個類B的所有功能都暴露給了外部,這樣是不好的,我們可以修改為類A只持有需要功能的介面例項。因此需要將類B的所有功能拆分為N個介面,繼承介面並實現方法。

介面隔離原則是對介面進行規範約束,其包含以下4層含義:

  • 介面要儘量小:這是核心定義,不出現臃腫的介面,要儘量拆分為n個小介面。但是首先要滿足單一職責。
  • 介面要高內聚:提高介面,類,模組的處理能力,較少對外的互動。
  • 定製服務:單獨為一個個體提供優良的服務,只提供訪問者需要的方法。
  • 介面的設計是有限度的:介面的設計粒度越小,系統越靈活。但是靈活的同時,也帶來了結構的複雜化。那麼這個度如何判斷呢?沒有標準,看開發者的經驗和常識了。

總結就是:使用多個隔離的介面,比使用單個功能廣泛的介面要好。

6.迪米特法則,又稱最少知道原則(Demeter Principle)

一個物件應該對其他物件有最少的瞭解

一個實體應當儘量少地與其他實體之間發生相互作用,使得系統功能模組相對獨立。

迪米特法則對類的低耦合提出的明確的要求,包含了以下含義

  • 只和朋友交流:朋友類的定義是這樣的:出現在成員變數、方法的輸入輸出引數中的類稱為成員朋友類,而出現在方法體內部的類不屬於朋友類。
  • 朋友也是有距離的:迪米特法則要求類“羞澀”一點,儘量不要對外公佈太多的public方法和非靜態的public變數,儘量內斂,多使用private、package-private、protected等訪問許可權。
  • 是你自己的就是你自己的:在實際應用中經常會出現這樣一個方法:放在本類中也可以,放在其他類中也沒有錯,那怎麼去衡量呢?正確做法:如果一個方法放在本類中,既不增加類間關係,也對本類不產生負面影響,那就放置在本類中。

迪米特法則的核心觀念就是類間解耦,弱耦合,只有弱耦合了以後,類的複用率才可以提高。