設計模式之組合模式

在平常生活中存在不少部分和總體的關係,他們都具備一致性,可是有各有個的行爲,好比大學中的部門與學院、總公司的部門與分公司、學習用品中的書與書包、生活用品中的衣服與衣櫃,以及廚房中的鍋碗瓢盆等。設計模式

然而在項目開發中也存在相同的道理,當咱們作人員管理系統的時候,公司下面是部門,每一個部門下面可能會有分組,組以後纔是人員,那麼依照此例的話,就造成了一個樹狀的結構,以總公司爲根基,一層一層的向下遞歸。對這些簡單的對象來與複合對象的處理,用組合模式模式實現起來可能會更加的恰當和方便。安全

什麼是組合模式

組合模式:組合模式,將對象組合成樹形結構以表示「部分-總體」的層次結構。用戶對單個對象和組合對象的使用具備一致性。 —— 節選自百度百科學習

其實在文章開頭已經說了比較詳盡了,組合模式經過建立對象的組的樹形結構,把對象組合成樹狀結構以表示總體及部分的構建關係。組合模式一句樹狀結構把對象進行組合,以用來表示部分以及總體之間的層次關係,這種類型的設計模式隸屬於結構型設計模式,組合模式使用戶對單個對象和組合對象的訪問都具有一致性。可讓客戶端用同一種方式處理個別對象以及對象組合。當咱們的業務屬於樹狀結構的時候,就能夠適當的考慮使用組合模式。this

組合模式優缺點

組合模式中包含一個容器,然而這個容器是由多個包括容器在內的子容器所構成的,組合模式與其子類都擁有相同的方法。可是,組合模式自己並不完成其中具體的細節的一些工做,而是把這些請求遞歸的傳給本身的子類型,最終完成後獲取到最終想要的結果。spa

優勢
  1. 組合模式是得客戶端代碼能夠一致的處理單個對象和組合對象,並不須要關心本身處理的是單個對象仍是組合對象,大大的簡化了客戶端的代碼
  2. 組合模式能夠更容易的在組合類中加如新的對象,客戶端不會由於加入了新的對象而更改源代碼,從何知足開閉原則
  3. 使用組合模式時能夠利用多態和遞歸的機制更加方便的使用複雜樹構建
缺點
  1. 客戶端須要花更多的時間理清類與類之間的層次關係,增大了客戶端的複雜程度
  2. 不容易限制容器中的組成部分
  3. 不太可以用繼承的方式增長構件的新功能

示例

筆者在翻閱資料的時候,發現組合模式又分紅兩種,分別是透明模式和安全模式。設計

組合模式的主要角色以下:code

  1. 組件:接口描述了樹中簡單項目和複雜項目所共有的操做。
  2. 葉節點:是樹的基本結構,它不包含子項目。通常狀況下,葉節點最終會完成大部分的實際工做,覺得他們沒法將工做指派給其餘部分。
  3. 容器:包含葉節點或其餘容器等子項目的代爲。容器指導其子項目所屬的具體類,它只經過通用的組件接口與其子項目交互。
  4. 客戶端:經過組件接口與全部項目交互,所以客戶端以相同方式與樹狀結構中的簡單或複雜項目交互。
透明模式

透明模式是把組合使用的方法放到抽象類中,無論葉子對象仍是容器都有相同的結構,這樣作的好處就是葉子節點對與外界是沒有任何區別的,他們具有徹底一致的行爲接口。可是由於葉對象自己不存在添加和刪除功能,因此實現它是沒有意義的。component

類圖以下所示:對象

image

代碼示例:blog

abstract class Component {

  protected name:string;

  constructor(name:string){
    this.name = name;
  }

  //  增長一個葉子構件或樹枝構件
  public abstract add(component:Component):void;

  //  刪除一個葉子構件或樹枝構件
  public abstract remove(component:Component):void;

  //  獲取分支下的全部葉子構件和樹枝構件
  public abstract display(depth:number):void;

}

class Composite extends Component {

  private componentArrayList:Component[] = []

  constructor(name:string){
    super(name);
  }

  public add(component:Component): void {
    this.componentArrayList.push(component);
  }

  public remove(component:Component): void {
    const {componentArrayList} = this;
    this.componentArrayList = componentArrayList.filter((el:Component) => component === el);
  }

  public display(depth:number):void {
    //  輸出樹形結構
    for(let i = 0; i < depth; i++) {
      console.log("------")
    }
    console.log(this.name);
    const {componentArrayList} = this;
    //  遍歷下一級
    for (let i = 0,item:Component ;item = componentArrayList[i++];) {
      item.display(depth + 1);
    }
  }
}

class Leaf extends Component {
  
  constructor(name:string){
    super(name);
  }

  public add(component:Component): void {
    throw new Error("Unsupported request");
  }

  public remove(component:Component): void {
    throw new Error("Unsupported request");
  }

  //  輸出樹形結構的葉子節點
  public display(depth:number): void {
    for(let i = 0; i < depth; i++) {
      console.log('-');
    }
    console.log(this.name);
  }

}

const compositeOne = new Composite("CompositeOne");
const leafOne = new Leaf("CompositeOne-01");
compositeOne.add(leafOne);
compositeOne.add(new Leaf("CompositeOne-02"));
compositeOne.add(new Leaf("CompositeOne-03"));
compositeOne.display(1);
安全模式

安全模式是把樹枝節點和樹葉節點完全分開,樹枝節點單獨又有用來組合的方法,這種方法比較安全。可是因爲不夠透明,因此樹葉節點和樹枝節點將具備不一樣的接口,客戶端的調用須要作相應的判斷,帶來了很大的不便。

類圖以下所示:

image

代碼示例:

abstract class Component {
  protected name:string;

  constructor(name:string){
    this.name = name;
  }

  public abstract display(depth:number):void;
};

class Composite extends Component {

  private componentArrayList:Component[] = [];

  constructor(name:string){
    super(name);
  }

  public add(component:Component): void {
    this.componentArrayList.push(component);
  }

  public remove(component:Component): void {
    const {componentArrayList} = this;
    this.componentArrayList = componentArrayList.filter((el:Component) => component === el);
  }

  public display(depth:number):void {
    //  輸出樹形結構
    for(let i = 0; i < depth; i++) {
      console.log("------")
    }
    console.log(this.name);
    const {componentArrayList} = this;
    //  遍歷下一級
    for (let i = 0,item:Component ;item = componentArrayList[i++];) {
      item.display(depth + 1);
    }
  }

};

class Leaf extends Component {

  constructor(name:string){
    super(name);
  }

  public display(depth:number):void {
    //輸出樹形結構的葉子節點
    for(let i=0; i<depth; i++) {
      console.log('-');
    }
    console.log(this.name);
  }

}

const compositeOne = new Composite("CompositeOne");
const leafOne = new Leaf("CompositeOne-01");
compositeOne.add(leafOne);
compositeOne.add(new Leaf("CompositeOne-02"));
compositeOne.add(new Leaf("CompositeOne-03"));
compositeOne.display(1);
小節

經過上述兩種方法運行結果均是相同的,卻別在於內部實現的而已,一種是葉節點與樹枝節點具有一致的行爲接口但有空實現的透明模式,另外一種是樹枝節點單獨擁有用來組合的方法但調用不便的安全模式。爲何說它調用不便呢,由於咱們若是經過遞歸遍歷樹時,這時須要判斷當前節點是葉子節點仍是樹枝節點,客戶端就須要相應的判斷。

總結

文章中分析了組合模式的結構和特色(組合模式用於組合多個對象造成樹形結構以表示具備部分-總體關係地層次結構。包含抽象構件類,葉子構件類,容器構件類),綜上所述在業務中須要表示一個對象總體與部分層次結構的場合的時候可使用組合模式,當對用戶隱藏組合對象與單個對象的不一樣,用戶能夠用統一的組合結構的對象的時候一樣也可使用組合模式。

相關文章
相關標籤/搜索