"环环相扣",聊聊组合模式

118 阅读3分钟

设计模式-组合模式

何为组合?

偶像组合?
No,No,No!!!
这里是组合指的是一个类使用另外一个类成为他的成员对象
它是代码重用的两种解决途径之一,另外一种是继承

而组合模式的组合,可以说是总-分结构,也就是总组合分
当然,这是从实际的类关系来看,从形式来看是相同的接口

  public class 总{
    private 分 分名;
}

现实例子

为了方便管理和沟通,组合的这种逻辑关系在社会中普遍存在
如地方行政制度,郡县制

junxian.jpg
假设限制皇帝发布一个政令,如果需要独自跑到每个里去颁布会产生很多问题,

1.效率问题
个人的时间是很有限的,如果皇帝每次都亲力亲为到底层工作,恐怕大部分时间都会浪费在路上
2.沟通问题
皇帝与里典存在明显的阶级差别,很难直接进行沟通

但如果皇帝向中央机构直接交流,就能完美地解决以上两个问题,其实这和封装思想很像。

代码结构

Composite.jpg

Component组件接口

抽象组合对象的方法,客户端统一的访问接口

内容
  1. 未实现的增/删成员对象方法
  2. 未实现的业务方法

Composite 组件类

实现了组件接口,具有组合能力的节点类

内容
  1. 类型为组件接口的成员对象集合(常实现为List)
  2. 已实现增/删成员对象方法
  3. 实现的业务方法(遍历成员对象集合,进行递归调用它们的业务方法)

Leaf 叶子类

实现了组件接口,不具备具有组合能力的最终类

内容
  1. 空实现增/删成员对象方法
  2. 实现的业务方法

实例代码

这里以前面提到的地方行政制度为例

Place 地方接口

/**
 * 地方接口,对应Component组件接口
 */
public interface Place {
    void addSubPlace(Place place);
    void deleteSubPlace(Place place);
    void showStruct(Integer layer);
}

Place 地方接口

/**
 * 大地方类
 * 对应Composite 组件类
 */
public class BigPlace implements  Place{
    String name;
    // 组合
    List<Place> subPlaceList = new LinkedList<>();

    public BigPlace(String name) {
        this.name = name;
    }

    @Override
    public void addSubPlace(Place place) {
        subPlaceList.add(place);
    }

    @Override
    public void deleteSubPlace(Place place) {
        subPlaceList.remove(place);
    }

    /**
     * 业务方法
     * 根据layer层次输出"-"
     * 每次组合层数加一
     * @param layer
     */
    @Override
    public void showStruct(Integer layer) {
        for (int i = 0; i < layer; i++) {
            System.out.print("-");
        }
        System.out.println(name);
        Iterator<Place> iterator = subPlaceList.listIterator();
        while(iterator.hasNext()){
            iterator.next().showStruct(layer+1);
        }
    }
}

LocalPlace 本地类

/**
 * 本地类
 * 对应叶子类
 */
public class LocalPlace implements Place{
    String name;

    public LocalPlace(String name) {
        this.name = name;
    }

    @Override
    public void addSubPlace(Place place) {
    }

    @Override
    public void deleteSubPlace(Place place) {
    }

    @Override
    public void showStruct(Integer layer) {
        for (int i = 0; i < layer; i++) {
            System.out.print("-");
        }
        System.out.println(name);
    }
}

调用与结果

Place china = new BigPlace("中国");
china.addSubPlace(new LocalPlace("上海市"));
china.addSubPlace(new LocalPlace("香港特别行政区"));

Place GDProvince = new BigPlace("广东省");
GDProvince.addSubPlace(new LocalPlace("广州市"));
GDProvince.addSubPlace(new LocalPlace("深圳市"));
china.addSubPlace(GDProvince);

china.showStruct(1);

System.out.println("-------------");

GDProvince.showStruct(1);

image.png
可以看到对于客户端来说,
只需要接触Place接口,完成组合关系,对于任意的Place都可以完成业务方法的调用

适用场景

  1. 存在多层的组合关系(树状关系)
  2. 想要忽略节点与叶子之间的差别,统一调用

与观察者模式的关系

组合模式像是一种扩展的观察者模式

观察者的代码结构

image.png
颁布者维护订阅者集合,当条件满足时候,遍历所有订阅者,调用订阅者的通知方法。

相同点

都通过组合,维护了一个成员对象集合
遍历集合,来进行间接调用。

不同点

  • 观察者模式中,调用方与被调用方可以不是相同的接口
    组合模式中,调用方与被调用方实现了相同的接口,说明了调用方与被调用方的地位是对等
  • 观察者模式中,被调用方一般不会继续调用,是两层的调用结构
    组合模式中,被调用方可能还会继续调用,是多层的调用结构。

思维导图

image.png