UML類圖中的五種關係的耦合強弱比較:依賴<關聯<聚合<組合<繼承

一、依賴關係:

(一)說明

虛線+箭頭

可描述為:Uses a

依賴是類的五種關係中耦合最小的一種關係。

因為在生成程式碼的時候,這兩個關係類都不會增加屬性

(二)依賴關係圖與程式碼的對應關係


PS:依賴關係:Animal依賴於Water(動物依賴於水))

  1. Public class Animal()  
  2. {  
  3.         Public Animal(){}  
  4. }  
  5. Public class Water()  
  6. {  
  7.         public Water(){}  
  8. }  
可以看到生成的兩個類的程式碼中什麼都沒有新增

(三)思考:

Animal類如何使用Water類呢?或者說依賴關係到底是如何體現的呢?

1、表現形式1

Water類是全域性的,則Animal類可以呼叫它

2、表現形式2

Water類是 Animal類的某個方法中的變數,則Animal類可以呼叫它。

  1. Public class Animal {  
  2.       Public void Grownup() {  
  3.                Water water =null;  
  4.       }  
  5. }  

注意1 Water類的生命期,它是Animal類的GrounUp方法被呼叫的時候,才被例項化

注意2持有Water類的是Animal的一個方法而不是Animal,這點是最重要的!

3、表現形式3

Water類是作為Animal類中某個方法的引數或者返回值

  1. Public Animal {  
  2.    Public Water Grownup(Waterwater) {  
  3.               returnnull;  
  4.       }  
  5. }  

注意:Water類被Animal類的一個方法持有。生命期隨著方法的執行結束而結束

二、關聯關係

(一)說明

實線+箭頭

可描述為:Has a

關聯關係用實線,表示類之間的耦合度比依賴強

在生成程式碼的時候,關聯關係的類會增加屬性。

(二)關聯關係與程式碼的對應關係

      

           PS:Water類與Climate類關聯(水與氣候關聯)。

  1. Public classWater {  
  2.      public Climate m_Climate;  
  3.      public Water(){}  
  4. }  
  5. Public class Climate {  
  6.      public Climate() {}  
  7. }  
可見生成的程式碼中,Water類的屬性中增加了Climate類。

(三)關聯關係的種類

關聯既有單向關聯又有雙向關聯。

1、單向關聯: Water類和Climate類單向關聯(如下圖),則Water類稱為源類,Climate類稱為目標類。源類瞭解目標類的所有的屬性和方法,但目標類並不瞭解源類的資訊。

     

2、雙向關聯:源類和目標類相互瞭解彼此的資訊。如將Water類和Climate類之間改為雙向關聯。


  1. Public class Water {  
  2.     public Climate m_Climate;  
  3.     public Water(){}  
  4. }  
  5. Public class Climate {  
  6.     public Water m_Water;  
  7.     public Climate() {}  
  8. }  

可見生成的程式碼中,兩個類的屬性都添加了!

(四)思考:

依賴關係和關聯關係的區別在哪裡?

1、從類的屬性是否增加的角度看

1)發生依賴關係的兩個類都不會增加屬性。其中的一個類作為另一個類的方法的引數或者返回值,或者是某個方法的變數而已。

2)發生關聯關係的兩個類,其中的一個類成為另一個類的屬性,而屬性是一種更為緊密的耦合,更為長久的持有關係。

2、從關係的生命期角度看:

1)依賴關係是僅當類的方法被呼叫時而產生,伴隨著方法的結束而結束了。

2)關聯關係是當類例項化的時候即產生,當類銷燬的時候,關係結束。相比依賴講,關聯關係的生存期更長。

(五)關聯關係的細化:聚合、組合

1、說明

1)聚合關係,用空心菱形加箭頭表示

2)組合關係,用實心菱形加箭頭表示,類之間的耦合關係比聚合強!

2聚合和組合都是關聯關係的一種,到底如何區分二者呢?

1)聚合和組合生成的程式碼


         (PS:此圖表明雁群類是由大雁類聚合而成)

  1. Public classGooseGroup {  
  2.     public Goose goose;  
  3.     Public GooseGroup(Goose goose) {  
  4.               this.goose = goose;  
  5.        }  
  6. }  

        (PS:此圖表明大雁類是由翅膀類組合而成)

  1. Public classGoose {  
  2.    public Wings wings;  
  3.    public Goose() {  
  4.        wings = new Wings();  
  5.     }  
  6. }  

2建構函式不同

聚合類的建構函式中包含了另一個類作為引數。 雁群類(GooseGroup)的構造函式中要用到大雁(Goose)作為引數傳遞進來。大雁類(Goose)可以脫離雁群類而獨立存在。

         組合類的建構函式中包含了另一個類的例項化。 表明大雁類在例項化之前,一定要先例項化翅膀類(Wings),這兩個類緊密的耦合在一起,同生共滅。翅膀類(Wings)是不可以脫離大雁類(Goose)而獨立存在。

3資訊的封裝性不同。

         在聚合關係中,客戶端可以同時瞭解雁群類和大雁類,因為他們都是獨立的。

         在組合關係中,客戶端只認識大雁類,根本就不知道翅膀類的存在,因為翅膀類被嚴密的封裝在大雁類中。

三、泛化

(一)說明

實線+箭頭

可描述為:Is a

泛化也稱繼承,子類將繼承父類的所有屬性和方法,並且可以根據需要對父類進行拓展。

(二)泛化關係與程式碼的對應關係


PSBird類繼承Animal類,鳥是一種動物)

  1. Class  Bird :Animal{  
  2. }  

(三)思考:

1子類繼承父類,真的是繼承了父類的所有屬性和方法嗎?

子類確實是繼承了父類的所有屬性和方法,只是對於父類的私有型別成員沒有訪問許可權!訪問就會報錯!

2泛化和繼承是一回事兒嗎?

子類繼承父類,父類泛化子類。這兩個詞是從不同的角度來說的!

3為什麼要多用組合少用繼承?

       繼承和組合各有優缺點。

類繼承是在編譯時刻靜態定義的,且可直接使用,類繼承可以較方便地改變父類的實現。但是類繼承也有一些不足之處。首先,因為繼承在編譯時刻就定義了,所以無法在執行時刻改變從父類繼承的實現。更糟的是,父類通常至少定義了子類的部分行為,父類的任何改變都可能影響子類的行為。如果繼承下來的實現不適合解決新的問題,則父類必須重寫或被其他更適合的類替換。這種依賴關係限制了靈活性並最終限制了複用性。

物件組合是通過獲得對其他物件的引用而在執行時刻動態定義的。由於組合要求物件具有良好定義的介面,而且,物件只能通過介面訪問,所以我們並不破壞封裝性;只要型別一致,執行時刻還可以用一個物件來替代另一個物件;更進一步,因為物件的實現是基於介面寫的,所以實現上存在較少的依賴關係。

四、實現關係

       虛線+箭頭


 (PSWideGoose類實現IFly介面。大雁實現飛翔的介面)

  1. Class WideGoose:Ifly{   
  2. }  

實現關係重點理解介面的定義

介面interface),介面是一種特殊的抽象類,這種抽象類中只包含常量和方法的定義,而沒有變數和方法的實現。