1. 程式人生 > >第五週:面向物件部分內容總結(5)---java設計規則

第五週:面向物件部分內容總結(5)---java設計規則

面向物件設計原則

1、開閉原則

  • 開閉原則理解:

    簡單說就是一個軟體實體支援擴充套件,不支援修改。就是在不改變原始碼的基礎上,擴充套件其它的功能。

    其實筆者認為,開閉原則無非就是想表達這樣一層意思:用抽象構建框架,用實現擴充套件細節。因為抽象靈活性好,適應性廣,只要抽象的合理,可以基本保持軟體架構的穩定。而軟體中易變的細節,我們用從抽象派生的實現類來進行擴充套件,當軟體需要發生變化時,我們只需要根據需求重新派生一個實現類來擴充套件就可以了。當然前提是我們的抽象要合理,要對需求的變更有前瞻性和預見性才行。

  • 開閉原則實現:

    開閉原則主要實現於介面或抽象類,介面和抽象類只提供功能(即不可修改),功能的具體擴充套件需要定義實現類去實現(即可擴充套件)。

  • 開閉功能的好處:

    通過擴充套件已有軟體系統,可提供新的行為,以滿足對軟體的新需求,提高了軟體系統的適應性和靈活性,特別是最重要的抽象層模組不能再修改,提高了軟體系統的一定的穩定性和延續性,這樣的設計同時也滿足了可複用性與可維護性;

2、單一職責

  • 系統中的每一個類都應該只有一個職責,而所有類所關注的就是自身職責的完成。

    一個類(或者大到模組,小到方法)承擔的職責越多,它被複用的可能性越小。而且如果一個類承擔的職責過多,就相當於將這些職責耦合在一起,當其中一個職責變化時,可能會影響其他職責的運作。

  • 為什麼要遵守單一職責原則?

    1、提高類的可維護性和可讀寫性

    一個類的職責少了,複雜度降低了,程式碼就少了,可讀性也就好了,可維護性自然就高了。

    2、提高系統的可維護性

    系統是由類組成的,每個類的可維護性高,相對來講整個系統的可維護性就高。當然,前提是系統的架構沒有問題。

    3、降低變更的風險

    一個類的職責越多,變更的可能性就更大,變更帶來的風險也就越大。

  • 如何遵守單一職責原則

    合理的職責分解,相同的職責放到一起,不同的職責分解到不同的介面和實現中去

 

3、里氏替換原則

里氏代換原則的定義是——子型別必須能夠替換掉他們的父型別。

如果一個軟體實體使用的是一個父類的話,那麼就肯定適用其子類,並且該軟體實體察覺不到父類物件和子類物件的區別。

舉個例子,有個很厲害的老木匠,周邊的鄰居都找他做傢俱,老木匠還有個大徒弟,不但學全了老木匠的本事還青出於藍而勝於藍。後來老木匠幹不動了,所以每次有人找上門來都是老木匠接活兒,大徒弟去做傢俱,而對做傢俱的人來說,他們並不關心具體誰做的,只要能拿到傢俱就好。

只有在滿足了里氏替換原則之後,也就是子類可以代替父類出現,並且不影響程式的功能時,父類才能真正得到複用,並且子類可以在父類的基礎上新增新的行為,面向物件思想中,父類和子類的繼承關係是抽象化的具體實現。

4、依賴倒轉原則

定義如下:

高層模組不應該依賴低層模組,它們都應該依賴抽象。抽象不應該依賴於細節,細節應該依賴於抽象。

另一種表述為:要針對介面程式設計,不要針對實現程式設計。

 1  public class Driver {
 2      //司機的主要職責就是駕駛汽車
 3      public void drive(ICar car){
 4          car.run();
 5      }
 6  }
 7  //將汽車模組抽象為一個介面:可以是賓士汽車,也可以是寶馬汽車
 8  public interface ICar {
 9        //是汽車就應該能跑
10        public void run();
11  }
12  public class Benz implements ICar{
13      //汽車肯定會跑
14      public void run(){
15          System.out.println("賓士汽車開始執行...");
16    }
17  }
18  public class BMW implements ICar{
19     //寶馬車當然也可以開動了
20     public void run(){
21        System.out.println("寶馬汽車開始執行...");
22     }
23  }
24  //高層模組
25  public class Client {
26       public static void main(String[] args) {
27         IDriver xiaoLi = new Driver();
28         ICar benz = new Benz();
29        //小李開賓士車
30        xiaoLi.drive(benz);
31     }
32  }
33  ​

 

面向介面程式設計,司機依賴Icar介面,而不依賴實現類

5、介面隔離原則

1、客戶端不應依賴它不需要的介面

2、類間的依賴關係應該建立在最小的介面上

  其實通俗來理解就是,不要在一個接口裡面放很多的方法,這樣會顯得這個類很臃腫。介面應該儘量細化,一個介面對應一個功能模組,同時接口裡面的方法應該儘可能的少,使介面更加靈活輕便。

或許有的人認為介面隔離原則和單一職責原則很像,但兩個原則還是存在著明顯的區別。單一職責原則是在業務邏輯上的劃分,注重的是職責。介面隔離原則是基於介面設計考慮。

例如一個介面的職責包含10個方法,這10個方法都放在同一介面中,並且提供給多個模組呼叫,但不同模組需要依賴的方法是不一樣的,這時模組為了實現自己的功能就不得不實現一些對其沒有意義的方法,這樣的設計是不符合介面隔離原則的。介面隔離原則要求"儘量使用多個專門的介面"專門提供給不同的模組。

6、合成複用原則

合成複用原則定義

合成複用原則(Composite Reuse Principle, CRP)又稱為組合/聚合複用原則(Composition/ Aggregate Reuse Principle, CARP),其定義如下:

儘量使用物件組合,而不是繼承來達到複用的目的。

 

包括組合關係和聚合關係)來使用一些已有的物件,使之成為新物件的一部分;新物件通過委派呼叫已有物件的方法達到複用其已有功能的目的。簡言之:要儘量使用組合/聚合關係,少用繼承。

 

在面向物件設計中,可以通過兩種基本方法在不同的環境中複用已有的設計和實現,即通過組合/聚合關係或通過繼承。

繼承複用:實現簡單,易於擴充套件。破壞系統的封裝性,從基類繼承而來的實現是靜態的,不可能在執行時發生改變,沒有足夠的靈活性;只能在有限的環境中使用。(“白箱”複用 )

組合/聚合複用:耦合度相對較低,選擇性地呼叫成員物件的操作;可以在執行時動態進行。(“黑箱”複用 )

7、迪米特法則

也叫最少知識原則。迪米特法則的定義是隻與你的直接朋友交談,不與"陌生人"說話。如果兩個軟體實體無須直接通訊,那麼就不應當發生直接的相互呼叫,可以通過第三方轉發該應用。其目的是降低類之間的耦合度,提高模組的相對獨立性。

  迪米特法則中的朋友是指:當前物件本身、當前物件的成員物件、當前物件所建立的物件、當前物件的方法引數等,這些物件存在關聯、聚合或組合關係,可以直接訪問這些物件的方法。

優點:

  1、降低類之間的耦合度,提高模組的相對獨立性。

  2、由於親和度降低,從而提高了類的可複用率和系統的擴充套件性。

缺點:

  過度使用迪米特法則會使系統產生大量的中介類,從而增加系統的複雜性,使模組之間的通訊效率降低。所以,在釆用迪米特法則時需要反覆權衡,確保高內聚和低耦合的同時,保證系統的結構清晰。

使用迪米特法則需要注意:

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

  2、在類的結構設計上,儘量降低類成員的訪問許可權。

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

  4、在對其他類的引用上,將引用其他物件的次數降到最低。

  5、不暴露類的屬性成員,而應該提供相應的訪問器(set 和 get 方法)。

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

經典案例:

  明星與經紀人的關係例項。明星負責演出,經紀人負責處理日常事務,如與粉絲的見面會,與媒體公司的業務洽淡