在一個新的對象裏面使用一些已有的對象,使之成爲新對象的一部分;新的對象通過向這些對象的委派達到複用這些對象的目的。

如果兩個類是“Has-a”關係應使用合成、聚合,如果是“Is-a”關係可使用繼承。”Is-A”是嚴格的分類學意義上定義,意思是一個類是另一個類的”一種”。而”Has-A”則不同,它表示某一個角色具有某一項責任。

什麼是合成?什麼是聚合?

合成(Composition)和聚合(Aggregation)都是關聯(Association)的特殊種類。

聚合

聚合用來表示“擁有”關係或者整體與部分的關係。代表部分的對象有可能會被多個代表整體的對象所共享,而且不一定會隨着某個代表整體的對象被銷燬或破壞而被銷燬或破壞,部分的生命週期可以超越整體。例如,班級和學生,當班級刪除後,學生還能存在,學生可以被培訓機構引用。在設計中, 聚合不應該頻繁出現,這樣會增大設計的耦合度。

聚合關係UML類圖

class Student {

}

class Classes{

         private Student student;

         publicClasses(Student student){

                   this.student=student;
        }

}

合成

合成用來表示一種強得多的“擁有”關係。在一個合成關係裏,部分和整體的生命週期是一樣的。一個合成的新對象完全擁有對其組成部分的支配權,包括它們的創建和湮滅等。使用程序語言的術語來說,合成而成的新對象對組成部分的內存分配、內存釋放有絕對的責任。一個合成關係的成分對象是不能與另一個合成關係共享的。

合成關係UML類圖

class Room{

         public Room createRoom(){
                   System.out.println(“創建房間”);
                   returnnew Room();
          } 
 }

class House{

         private Room room;

         public House(){ 
               room=new Room();
          }

         public void createHouse(){
                room.createRoom();
         } 
  }

換句話說,合成是值的聚合(Aggregation by Value),而一般說的聚合是引用的聚合(Aggregation by Reference)。

爲什麼使用合成/聚合複用,而不使用繼承複用?

在面向對象的設計裏,有兩種基本的方法可以在不同的環境中複用已有的設計和實現,即通過合成/聚合複用和通過繼承複用。兩者的特點和區別,優點和缺點如下。

合成/聚合複用

由於合成或聚合可以將已有對象納入到新對象中,使之成爲新對象的一部分,因此新對象可以調用已有對象的功能。這樣做的好處有

  • 新對象存取成分對象的唯一方法是通過成分對象的接口。

  • 這種複用是黑箱複用,因爲成分對象的內部細節是新對象看不見的。

  • 這種複用支持包裝。

  • 這種複用所需的依賴較少。

  • 每一個新的類可以將焦點集中到一個任務上。

  • 這種複用可以再運行時間內動態進行,新對象可以動態地引用與成分對象類型相同的對象。

一般而言,如果一個角色得到了更多的責任,那麼可以使用合成/聚合關係將新的責任委派到合適的對象。當然,這種複用也有缺點。最主要的缺點就是通過這種複用建造的系統會有較多的對象需要管理。

繼承複用

繼承複用通過擴展一個已有對象的實現來得到新的功能,基類明顯的捕獲共同的屬性和方法,而子類通過增加新的屬性和方法來擴展超類的實現。繼承是類型的複用。

繼承複用的優點

  • 新的實現較爲容易,因爲超類的大部分功能可以通過繼承關係自動進入子類。

  • 修改或擴展繼承而來的實現較爲容易。

繼承複用的缺點

  • 繼承複用破壞包裝,因爲繼承將超類的實現細節暴露給了子類。因爲超類的內部細節常常對子類是透明的,因此這種複用是透明的複用,又叫“白箱”複用。

  • 如果超類的實現改變了,那麼子類的實現也不得不發生改變。因此,當一個基類發生了改變時,這種改變會傳導到一級又一級的子類,使得設計師不得不相應的改變這些子類,以適應超類的變化。

  • 從超類繼承而來的實現是靜態的,不可能在運行時間內發生變化,因此沒有足夠的靈活性。

由於繼承複用有以上的缺點,所有儘量使用合成/聚合而不是繼承來達到對實現的複用,是非常重要的設計原則。

相關文章