组合模式

998 阅读5分钟

这是我参与2022首次更文挑战的第14天,活动详情查看:2022首次更文挑战

组合模式

概念

组合模式(Composite Pattern),又叫部分整体模式,主要是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。

这种模式创建了一个包含自己对象组的类。该类提供了修改相同对象组的方式。

组合模式一般用来表示整体和部分的关系,将对象组织到树形结构中,最顶层的节点也称为根节点,根节点下面包含子树和叶子节点,子树有包括子树和叶子节点,树形结构如下图所示:

image.png

通过上图可以看出,根节点和子树都是同一种数据类型,而叶子节点和子树从定义上来讲不是同一种类型,但是在组合模式中,根节点、子树、叶子节点都共同看成同一种数据类型,或者是同一种接口定义。

应用场景

  • 层级结构

  • 部门组织结构

  • 希望客户端可以忽略组合对象与单个对象的差异

  • 对象层次具备整体和部分的关系,呈显树形结构

透明组合模式的写法

组合模式一般具有三种角色,分别是抽象根节点角色、树叶构件角色(叶子节点)、树枝构件角色(树枝节点),概念如下所示:

  • 抽象构件角色(Composite):定义系统歌层次对象的共有方法和属性,可以预先定义一些默认行为和属性,是组合中对象声明接口,实现所有类共有接口的默认行为。

  • 树枝构件角色(Composite):定义有树枝节点部件的组合部件行为,存储子部件,组合树枝节点和叶子节点形成一个树形结构。主要是在Component接口中实现与子部件有关的操作。

  • 树叶构件角色(Leaf):树叶构件角色就是叶子节点,下面没有其他的分支,是系统便利的最小层次。

一般情况下组合模式在代码的具体实现上分为两种,一种是透明组合模式,一直是安全组合模式。本主题主要讲透明组合模式。

透明组合模式就是把所有的公共方法都定义到Composite中,这样就不需要分辨那些事叶子节点,那些是树枝节点,他们具有统一的接口类型。

  • 抽象根节点(Composite)
public abstract class Component {
    protected String name;

    public Component(String name) {
        this.name = name;
    }
    public abstract String operation();

    public boolean addChild(Component component) {
        throw new UnsupportedOperationException("addChild not supported!");
    }
    public boolean removeChild(Component component) {
        throw new UnsupportedOperationException("removeChild not supported!");
    }
    public Component getChild(int index) {
        throw new UnsupportedOperationException("getChild not supported!");
    }
}
  • 树枝构件角色(Composite)
public class Composite extends Component {
    private List<Component> mComponents;

    public Composite(String name) {
        super(name);
        this.mComponents = new ArrayList<Component>();
    }

    @Override
    public String operation() {
        StringBuilder builder = new StringBuilder(this.name);
        for (Component component : this.mComponents) {
            builder.append("\n");
            builder.append(component.operation());
        }
        return builder.toString();
    }
    @Override
    public boolean addChild(Component component) {
        return this.mComponents.add(component);
    }

    @Override
    public boolean removeChild(Component component) {
        return this.mComponents.remove(component);
    }

    @Override
    public Component getChild(int index) {
        return this.mComponents.get(index);
    }
}
  • 树叶构件角色(Leaf)叶子节点
public class Leaf extends Component {

    public Leaf(String name) {
        super(name);
    }

    @Override
    public String operation() {
        return this.name;
    }
}
  • UML结构图

image.png

透明组合模式就是把所有的方法都添加到Component中,这样客户端就不需要知道哪些事叶子节点和树枝节点,但是透明组合模式就违反了设计模式的七大原则之一单一职责原则,即最少知道原则,然后叶子节点也会继承一些他不需要的的方法(管理其他操作的方法),这就违反了设计模式的原则之接口隔离原则。因此还有一种安全的写法就出现了,即安全组合模式。

安全组合模式的写法

安全组合模式是只规定系统各个层次的最基础的一致行为,而把组合本身的方法放到自身当中。

  • Component抽象根节点
public abstract class Component {
    protected String name;

    public Component(String name) {
        this.name = name;
    }
    public abstract String operation();
}
  • Composite(树枝组件)
public class Composite extends Component {
    private List<Component> mComponents;

    public Composite(String name) {
        super(name);
        this.mComponents = new ArrayList<Component>();
    }
    @Override
    public String operation() {
        StringBuilder builder = new StringBuilder(this.name);
        for (Component component : this.mComponents) {
            builder.append("\n");
            builder.append(component.operation());
        }
        return builder.toString();
    }
    public boolean addChild(Component component) {
        return this.mComponents.add(component);
    }
    public boolean removeChild(Component component) {
        return this.mComponents.remove(component);
    }
    public Component getChild(int index) {
        return this.mComponents.get(index);
    }

}
  • Leaf叶子节点
public class Leaf extends Component 
    public Leaf(String name) {
        super(name);
    }

    @Override
    public String operation() {
        return this.name;
    }
}
  • UML结构图

image.png

通过上述的代码和UML结构图可以知道,安全组合模式就是让接口定义指责清晰,同时符合设计模式的的单一职责原则和接口隔离原则,虽然安全组合模式符合设计原则,但是也有缺点,主要是客户需要区分树枝节点和叶子节点,才能正确的处理各个层次之间的操作,客户无法依赖抽象,就违背了设计模式的依赖倒置原则。

无论是透明组合模式还是安全组合模式都有各自的优点和缺点,通过自己的业务场景详情选择使用两种组合方法之一。

组合模式的优缺点

优点

  • 高层模块(客户端)调用简单。组合模式使得客户端代码可以一致地处理单个对象和组合对象,无须关心自己处理的是单个对象,还是组合对象,这简化了客户端代码

  • 节点自由增加,更容易在组合体内加入新的对象,客户端不会因为加入了新的对象而更改源代码,满足“开闭原则”

缺点

  • 在使用组合模式时,其叶子和树枝的声明都是实现类,而不是接口,违反了依赖倒置原则

  • 设计较复杂,客户端需要花更多时间理清类之间的层次关系

  • 不容易限制容器中的构件

  • 不容易用继承的方法来增加构件的新功能