木灵鱼儿

木灵鱼儿

阅读:486

最后更新:2022/06/13/ 1:20:04

结构型模式:组合模式

简介

组合模式它本身是一种树形结构模式,可以将其理解为一棵树,其中树为根节点,然后有树枝和树叶,树枝不断的分叉,树枝上也会有树叶。

我们看个图:

它是一种将对象组合成树状的层次结构的模式,用来表示“整体-部分”的关系,使用户对单个对象和组合对象具有一致的访问性。

其实说人话就是,我定义了一个接口,它有一个show方法,然后所有的对象都得实现,然后对象之间可以嵌套,因为嵌套了,所以当我调用show方法的时候,这个对象必须去遍历自己的子级嵌套对象的show方法,不断的重复,最终得到结果之和。

类似于一个递归函数,只不过具体的处理都在每个对象自身show方法中去实现。

组合模式常常用在:购物车、节点统计

购物车中最外层是根节点,然后每个店铺为一个树枝,每个商品为一个树叶,他们组合嵌套。

节点统计比如全国的人数统计,我们从:省 ——> 市 ——> 县 ——> 乡;这么一个层级结构,统计时我们只需要调用省的统计方法,它就会触发到市的统计方法,市的统计方法再到县,县再到乡,这样的话我们可以很好的统计的人数,且结构清晰明了,哪怕新增省市或者县乡都不会对原有统计方式破坏性改动,符合开闭原则。

代码实现

//购物车节点的抽象
interface Shopping {
  getPrice(): number; //获取价格
}

//单个商品的具体实现:树叶
class AGoods implements Shopping {
  name = "A商品";
  price = 100;

  getPrice() {
    return this.price;
  }
}

class BGoods implements Shopping {
  name = "A商品";
  price = 70;

  getPrice() {
    return this.price;
  }
}

//店铺的具体实现:树枝
class Store implements Shopping {
  name = "店铺1";
  goodsArr: Array<Shopping> = []; //商品数组

  //添加新商品
  add(goods: Shopping) {
    this.goodsArr.push(goods);
    return this;
  }

  //移除商品
  remove(goods: Shopping) {
    this.goodsArr = this.goodsArr.filter((item) => item !== goods);
  }

  //获取价格
  getPrice() {
    let total = 0;
    this.goodsArr.forEach((item) => {
      total += item.getPrice();
    });
    return total;
  }
}

//购物车:具体使用
class ShoppingCart {
  constructor() {
    //创建一个根节点
    const store = new Store();
    //增加一个店铺
    const d = new Store();

    store.add(d);

    //用户的商品
    const a = new AGoods();
    const b = new BGoods();

    //添加商品
    d.add(a).add(b);

    //获取总价
    console.log(store.getPrice());
  }
}

new ShoppingCart();

代码还是比较简单,意思就是这样,具体的业务逻辑肯定会更加复杂。

因为树枝自身就可以进行嵌套,它可以接受树枝和树叶,所以第一个树枝可以称之为根节点,加上我们遵循Shopping的规则要求都会实现getPrice方法,所以使用的时候没有多余的心智负担,用就完事了。

扩展

上述代码你会发现,树枝的规则要求非常薄弱,我们只是要求的getPrice方法实现,这显然比如后来的人可能约束不到位,所以我们将组合模式扩展一些,这个扩展被称为复杂的组合模式,当然这名称无关紧要,重要的是我们扩展的原因。

实际情况下,树枝和树叶可能各自都有自己的一些具体的方法,这些方法可能需要有,但是没有规则的约束,可能会导致代码的不健全,有的人总是会忘记需要声明对应的方法。

解决办法就是再声明一个树叶和树枝自己的抽象。

商品的抽象

//购物车节点的抽象
interface Shopping {
  getPrice(): number; //获取价格
}

//商品的抽象
abstract class Goods implements Shopping {
  abstract name: string;
  abstract price: number;

  abstract getPrice(): number;
}

//单个商品的具体实现:树叶
class AGoods extends Goods {
  name = "A商品";
  price = 100;

  getPrice() {
    return this.price;
  }
}

class BGoods extends Goods {
  name = "A商品";
  price = 70;

  getPrice() {
    return this.price;
  }
}

店铺的抽象

//店铺的抽象
abstract class Store implements Shopping {
  abstract name: string;
  abstract goodsArr: Array<Shopping>;

  //添加新商品
  add(goods: Shopping) {
    this.goodsArr.push(goods);
    return this;
  }

  //获取价格
  getPrice() {
    let total = 0;
    this.goodsArr.forEach((item) => {
      total += item.getPrice();
    });
    return total;
  }

  //移除商品
  abstract remove(goods: Shopping): void;
}

//店铺的具体实现:树枝
class Stores extends Store {
  name = "店铺1";
  goodsArr: Array<Shopping> = []; //商品数组

  //移除商品
  remove(goods: Shopping) {
    this.goodsArr = this.goodsArr.filter((item) => item !== goods);
  }
}

使用

//购物车:具体使用
class ShoppingCart {
  constructor() {
    //创建一个根节点
    const store = new Stores();
    //增加一个店铺
    const d = new Stores();

    store.add(d);

    //用户的商品
    const a = new AGoods();
    const b = new BGoods();

    //添加商品
    d.add(a).add(b);

    //获取总价
    console.log(store.getPrice());
  }
}

new ShoppingCart();

如果代码不明白可以看下这个类图:

版权申明

本文系作者 @木灵鱼儿 原创发布在木灵鱼儿 - 有梦就能远航站点。未经许可,禁止转载。

关于作者

站点职位 博主
获得点赞 0
文章被阅读 486

相关文章