Composite

设计模式目录

组合模式是一种结构型设计模式, 你可以使用它将对象组合成树状结构, 并且能像使用独立对象一样使用它们。

也可以称为整体-部分(Part-Whole)模式,它的宗旨是通过将单个对象(叶子节点)和组合对象(树枝节点)用相同的接口进行表示,使得客户对单个对象和组合对象的使用具有一致性。

目录系统就是一个典型的例子。

组合模式结构

样例

借助组合模式实现一系列复杂几何图形

// 组件接口会声明组合中简单和复杂对象的通用操作。
interface Graphic is
    method move(x, y)
    method draw()

// 叶节点类代表组合的终端对象。叶节点对象中不能包含任何子对象。叶节点对象
// 通常会完成实际的工作,组合对象则仅会将工作委派给自己的子部件。
class Dot implements Graphic is
    field x, y

    constructor Dot(x, y) { ... }

    method move(x, y) is
        this.x += x, this.y += y

    method draw() is
        // 在坐标位置(X,Y)处绘制一个点。

// 所有组件类都可以扩展其他组件。
class Circle extends Dot is
    field radius

    constructor Circle(x, y, radius) { ... }

    method draw() is
        // 在坐标位置(X,Y)处绘制一个半径为 R 的圆。

// 组合类表示可能包含子项目的复杂组件。组合对象通常会将实际工作委派给子项
// 目,然后“汇总”结果。
class CompoundGraphic implements Graphic is
    field children: array of Graphic

    // 组合对象可在其项目列表中添加或移除其他组件(简单的或复杂的皆可)。
    method add(child: Graphic) is
        // 在子项目数组中添加一个子项目。

    method remove(child: Graphic) is
        // 从子项目数组中移除一个子项目。

    method move(x, y) is
        foreach (child in children) do
            child.move(x, y)

    // 组合会以特定的方式执行其主要逻辑。它会递归遍历所有子项目,并收集和
    // 汇总其结果。由于组合的子项目也会将调用传递给自己的子项目,以此类推,
    // 最后组合将会完成整个对象树的遍历工作。
    method draw() is
        // 1. 对于每个子部件:
        //     - 绘制该部件。
        //     - 更新边框坐标。
        // 2. 根据边框坐标绘制一个虚线长方形。


// 客户端代码会通过基础接口与所有组件进行交互。这样一来,客户端代码便可同
// 时支持简单叶节点组件和复杂组件。
class ImageEditor is
    field all: array of Graphic

    method load() is
        all = new CompoundGraphic()
        all.add(new Dot(1, 2))
        all.add(new Circle(5, 3, 10))
        // ...

    // 将所需组件组合为复杂的组合组件。
    method groupSelected(components: array of Graphic) is
        group = new CompoundGraphic()
        foreach (component in components) do
            group.add(component)
            all.remove(component)
        all.add(group)
        // 所有组件都将被绘制。
        all.draw()

适用场景

  • 如果你需要实现树状对象结构, 可以使用组合模式。

    组合模式为你提供了两种共享公共接口的基本元素类型: 简单叶节点和复杂容器。 容器中可以包含叶节点和其他容器。 这使得你可以构建树状嵌套递归对象结构。

  • 如果你希望客户端代码以相同方式处理简单和复杂元素, 可以使用该模式。

    组合模式中定义的所有元素共用同一个接口。 在这一接口的帮助下, 客户端不必在意其所使用的对象的具体类。

实现方式

  1. 确保应用的核心模型能够以树状结构表示。 尝试将其分解为简单元素和容器。 记住, 容器必须能够同时包含简单元素和其他容器。

  2. 声明组件接口及其一系列方法, 这些方法对简单和复杂元素都有意义。

  3. 创建一个叶节点类表示简单元素。 程序中可以有多个不同的叶节点类。

  4. 创建一个容器类表示复杂元素。 在该类中, 创建一个数组成员变量来存储对于其子元素的引用。 该数组必须能够同时保存叶节点和容器, 因此请确保将其声明为组合接口类型。

    实现组件接口方法时, 记住容器应该将大部分工作交给其子元素来完成。

  5. 最后, 在容器中定义添加和删除子元素的方法。

    记住, 这些操作可在组件接口中声明。 这将会违反_接口隔离原则_, 因为叶节点类中的这些方法为空。 但是, 这可以让客户端无差别地访问所有元素, 即使是组成树状结构的元素。

组合模式优点

  • 你可以利用多态和递归机制更方便地使用复杂树结构。
  • 开闭原则。 无需更改现有代码, 你就可以在应用中添加新元素, 使其成为对象树的一部分。

组合模式缺点

  • 对于功能差异较大的类, 提供公共接口或许会有困难。 在特定情况下, 你需要过度一般化组件接口, 使其变得令人难以理解。
  • 组合模式在具体实现上违背了设计模式 接口隔离原则依赖倒置原则
posted @ 2021-01-18 19:37  花染梦  阅读(543)  评论(0编辑  收藏  举报