设计模式之组合模式

在平常生活中存在不少部分和总体的关系,他们都具备一致性,可是有各有个的行为,好比大学中的部门与学院、总公司的部门与分公司、学习用品中的书与书包、生活用品中的衣服与衣柜,以及厨房中的锅碗瓢盆等。设计模式

然而在项目开发中也存在相同的道理,当咱们作人员管理系统的时候,公司下面是部门,每一个部门下面可能会有分组,组以后才是人员,那么依照此例的话,就造成了一个树状的结构,以总公司为根基,一层一层的向下递归。对这些简单的对象来与复合对象的处理,用组合模式模式实现起来可能会更加的恰当和方便。安全

什么是组合模式

组合模式:组合模式,将对象组合成树形结构以表示“部分-总体”的层次结构。用户对单个对象和组合对象的使用具备一致性。 —— 节选自百度百科学习

其实在文章开头已经说了比较详尽了,组合模式经过建立对象的组的树形结构,把对象组合成树状结构以表示总体及部分的构建关系。组合模式一句树状结构把对象进行组合,以用来表示部分以及总体之间的层次关系,这种类型的设计模式隶属于结构型设计模式,组合模式使用户对单个对象和组合对象的访问都具有一致性。可让客户端用同一种方式处理个别对象以及对象组合。当咱们的业务属于树状结构的时候,就能够适当的考虑使用组合模式。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);
小节

经过上述两种方法运行结果均是相同的,却别在于内部实现的而已,一种是叶节点与树枝节点具有一致的行为接口但有空实现的透明模式,另外一种是树枝节点单独拥有用来组合的方法但调用不便的安全模式。为何说它调用不便呢,由于咱们若是经过递归遍历树时,这时须要判断当前节点是叶子节点仍是树枝节点,客户端就须要相应的判断。

总结

文章中分析了组合模式的结构和特色(组合模式用于组合多个对象造成树形结构以表示具备部分-总体关系地层次结构。包含抽象构件类,叶子构件类,容器构件类),综上所述在业务中须要表示一个对象总体与部分层次结构的场合的时候可使用组合模式,当对用户隐藏组合对象与单个对象的不一样,用户能够用统一的组合结构的对象的时候一样也可使用组合模式。

相关文章
相关标签/搜索