【浅谈设计模式】(11):组合模式--疫情统计案例的一次应用

【浅谈设计模式】(11):组合模式--疫情统计案例的一次应用

这是我参与8月更文挑战的第13天,活动详情查看:8月更文挑战

组合模式

前言

设计模式概览 今天我们来学习结构型模式的第六种----组合模式。

在探讨组合模式之前,先回顾几个概念,就是组合、继承、聚合。

  • 继承:它是 is a 的关系,对于接口来说是实现。例如 猫是动物。
  • 聚合:它体现整体和部分的关系,has a 的关系,例如:家里有只猫。这个整体和部分的关系不是强依赖的,家里也可以没有猫。
  • 组合:它同样体现整体和部分的关系,但这关系比聚合更强,它强调整体和部分的生命周期是一样的,整体生命周期结束也意味着部分的生命周期结束。

好了,下面正式学习组合模式。

一、组合模式入门

1.1 概述

组合模式(Composite Pattern)常常用来表达整体和部分的关系。而它也被常常用来表示树形菜单结构,我们可以通过这种模式将对象组合成树状结构,并且能够使用独立对象一样使用它们。

组合设计模式 那么具体如何理解呢,

首先,组合模式它通常使用一个通用接口来与产品盒子进行交互。比如一个产品内部是有很多黑盒组成,例如一个电脑由主机、键盘、显示器、鼠标构成。

电脑是个产品,这几个部件对应我们来说就是盒子。对于电脑,我们在里面声明一个计算总价的方法;对于盒子,我们就遍历所有盒子计算出鼠标、显示器、键盘的价格,如果主机是组装的,那它里面可能又有很多盒子,包括 内存条、硬盘等,由需要访问这些小黑盒的价格, 以这种递归的方式处理了电脑内部组成部分的价格,得到最终电脑的价格,而我们无需关注黑盒里面的复杂内容,只需调用相同的接口进行处理即可。类似下图的逻辑。

组合模式建议的解决方案

明白了组合模式的定义,我们来看看 维基百科的定义:

在软件工程中,组合模式是一种分区设计模式。组合模式描述了一组对象,它们的处理方式与相同类型对象的单个实例相同。复合的目的是将对象“组合”成树结构以表示部分-整体层次结构。实现复合模式可以让客户端统一处理单个对象和组合。

组合模式可以让客户统一处理单个对象和组合。就是与我们的常见的树形目录的组装处理是类似的。

由于最近的疫情反扑,每天都有着本土病例的新增。我现在看到的疫情数据如下,这里面我们以市级为单位做了个初步统计,如果要计算昨日中国本土病例的新增值。这里就可以用到组合模式。后面,我们用代码来演示下。

image-20210812144506469

image-20210812144439819

1.2 结构

1.2.1 角色

组合模式中主要包含三种角色:

  • 抽象根节点:定义系统各层次对象的共同方法和属性,可以预先定义一些默认行为和属性
  • 树枝节点:定义树枝节点的行为,存储子节点,组合树枝节点和叶子节点形成一个树形结构
  • 叶子节点:叶子节点对象,其下再无分支,是系统层次遍历的最小单位

img

1.2.2 模式分类

组合模式根据抽象构件类的定义形式,可以分为透明组合模式和安全组合模式两种形式:

  • 透明组合模式

    • 透明组合模式中,抽象根节点角色中声明了所有用于管理成员对象的方法,这样所有的树干对象和树叶对象都有相同的接口。
    • 但是缺点就是不够安全,比如添加子类,如果是树叶元素就不需要添加树叶的这个功能,但是它却有这个接口存在,如果运行时不小心调用可能会报错
  • 安全组合模式:

    • 在安全模式中,抽象构件角色中没有声明任何用于管理成员对象的方法,而是在树枝节点类中进行管理并实现这些方法,它的缺点就是不够透明,必须有区别的对待叶子构件和容器构件。

1.3 应用场景

上面分析了组合模式的角色与分类,下面分析下它的适用场景。

组合模式它是应对树形结构而生,所以组合模式的适用场景就是出现树形结构的地方,比如:文件目录显示,多级目录呈现等树形结构数据的操作。概括起来就是下面两种:

  • 在需要表示一个对象整体与部分的层次结构的场合
  • 在要求对用户用统一接口适用组合结构中的所有对象的场合。

1.4 优缺点

优点:

  • 组合模式可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,它让客户端忽略了层次的差异,方便对整个层次结构进行控制。
  • 客户端可以一致地使用一个组合结构或其中单个对象,不必关心处理的是单个对象还是整个组合结构,简化了客户端代码。
  • 在组合模式中增加新的树枝节点和叶子节点都很方便,无须对现有类库进行任何修改,符合“开闭原则”。
  • 组合模式为树形结构的面向对象实现提供了一种灵活的解决方案,通过叶子节点和树枝节点的递归组合,可以形成复杂的树形结构,但对树形结构的控制却非常简单。

缺点:

  • 客户端需要花更多的时间理清类之间的层次关系。
  • 不容易限制容器中的构建。
  • 不容易用继承的方法来增加构建的新功能。
  • 比较难限制组合中的组件类型。

二、案例讲解

接下来,我们把这个疫情统计的案例用组合模式实现一下吧。

这边先挑选两个省份和一个城市来做个演示。我们要计算出两个省份和一个城市的总感染人数。

测试类

public class Test {
    public static void main(String[] args) {
        /**
         *  筛选几个城市疫情数目,
         *  中国昨日本土疫情新增:
         *  |-- 江苏:38
         *      -- 扬州:37
         *      -- 南京: 1
         *  |-- 台湾:22
         *  |-- 湖北:10
         *      -- 武汉:4
         *      -- 荆门:6
         *  |-- ......
         */
        int sum =0;
        Component china = new Component("中国");
​
        Component js = new Component("江苏");
        City nj = new City(1, "南京");
        City yz = new City(37, "扬州");
        js.add(nj);
        js.add(yz);
​
        City tw = new City(22, "台湾");
​
        Component hb = new Component("湖北");
        City wh = new City(4, "武汉");
        City jm = new City(6, "荆门");
        hb.add(wh);
        hb.add(jm);
​
        china.add(js);
        china.add(tw);
        china.add(hb);
​
        System.out.println("昨日感染人数:"+china.count());
​
    }
}
​
复制代码

树叶构件:城市

/**
 * @Author xiaolei
 * 树叶构件:城市
 **/
public class City implements Counter{
​
    // 数量
    private int sum =0;
    // 名称
    private String name;
​
    public City(int sum,String name){
        this.sum=sum;
        this.name=name;
    }
​
    public int count() {
        return sum;
    }
}
复制代码

树干构件:容器

public class Component implements Counter{
​
    private String name;
    private ArrayList<Counter> arrayList =new ArrayList<Counter>();
​
    public Component(String name){
        this.name=name;
    }
​
    // 添加城市人数
    public void add(Counter counter){
        arrayList.add(counter);
    }
​
    // 计算
    public int count() {
        int sum=0;
        for (Counter counter : arrayList) {
            sum+=counter.count();
        }
        return sum;
    }
}
复制代码

抽象构件:

/**
* 统计接口
**/
public interface Counter {
    int count();
}
复制代码

输出结果:

昨日感染人数:70
复制代码

三、总结

组合模式处理的正是整体和部分的组合。如果树叶构件和树干构件有很多差异的话,比如很多属性和方法都不一样的时候,就不适用它了,而适用它可以非常方便的处理树形结构,本文中提到三个例子,一个是文件夹的目录层级结构,一个是电脑的价格计算,以及一个疫情感染统计计算。通过这些简单易懂的例子,可以帮助我们快速理解组合模式。

设计模式本身在应用中是有一定难度的,需要一定的业务经验积累,如果哪一天你在哪个底层源码中突然领略到哪个你看过的模式,就值了,如果哪一天,你在面对业务捉急的时候,突然把脑门一拍,哎,要不试试组合模式,那你可能就顿悟了,希望那一天离你更近一点。

最后,祝愿疫情早日结束!!!

互动环节

  • 如果我的文章对你有用,请给我点个👍,感谢你😊!
  • 有问题,欢迎在评论区指出!💪

四、浅谈设计模式系列👏👏👏

分类:
后端