设计模式:012_组合模式

64 阅读3分钟

组合模式

组合模式(Composite)又称部分-整体模式,将对象组合成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。

问题示例

按层级获取省市区的数据。

作用

  • 组合模式使得客户端代码可以一致地处理单个对象和组合对象,无须关心自己处理的是单个对象,还是组合对象,这简化了客户端代码
  • 更容易在组合体内加入新的对象,客户端不会因为加入了新的对象而更改源代码,满足“开闭原则”

缺点

  • 设计较复杂,客户端需要花更多时间理清类之间的层次关系。
  • 在增加新构件时很难对容器中的构件类型进行限制。

适用场景

  • 表示对象的部分-整体层次结构,如树形菜单,文件夹的管理,遍历组织架构,或者处理的对象具有树形结构,非常适合使用组合模式。
  • 希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。
  • 如果整体和部分有很多差异的话,比如很多方法和属性都不一样,不适合使用组合模式。

类图

组合模式类图.png

  • 抽象构件角色(Component):主要作用是为树叶构件和树枝构件声明公共接口,并实现它们的默认行为。
  • 树枝构件角色(Composite):是组合中的分支节点对象,它有子节点。它实现了抽象构件角色中声明的接口,它的主要作用是存储和管理子部件,通常包含 Add()/Remove()/GetChild() 等方法。
  • 树叶构件角色(Leaf):是组合中的叶节点对象,它没有子节点,用于实现抽象构件角色中声明的公共接口。

代码表达

// 抽象构件角色(Component)
public abstract class AddressComponent {
    private String name;

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void add(AddressComponent address) {
        // 默认实现,防止树叶构件角色调用
        throw new UnsupportedOperationException();
    }

    public void remove(AddressComponent address) {
        // 默认实现,防止树叶构件角色调用
        throw new UnsupportedOperationException();
    }

    // 所有子类需要实现的具体操作
    public abstract void print();
}
// 树枝构件角色(Composite)
public class Province extends AddressComponent {
    // 存储和管理子部件
    List<AddressComponent> list = new ArrayList<>();

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

    @Override
    public void add(AddressComponent address) {
        list.add(address);
    }

    @Override
    public void remove(AddressComponent address) {
        list.remove(address);
    }

    @Override
    public void print() {
        // 递归调用子部件的方法
        System.out.println("===" + getName() + "===");
        for (AddressComponent address : list) {
            address.print();
        }
    }
}

public class City extends AddressComponent {
    // 存储和管理子部件
    List<AddressComponent> list = new ArrayList<>();

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

    @Override
    public void add(AddressComponent address) {
        list.add(address);
    }

    @Override
    public void remove(AddressComponent address) {
        list.remove(address);
    }

    @Override
    public void print() {
        // 递归调用子部件的方法
        System.out.println("---" + getName() + "---");
        for (AddressComponent address : list) {
            address.print();
        }
    }
}
// 树叶构件角色(Leaf)
public class District extends AddressComponent {
    // 叶子节点没有子节点,不需要 add/remove 等管理方法
    public District(String name) {
        super(name);
    }

    @Override
    public void print() {
        System.out.println(getName());
    }
}

客户端调用

AddressComponent guangdong = new Province("guangdong");
AddressComponent guangzhou = new City("guangzhou");
AddressComponent shenzhen = new City("shenzhen");
guangdong.add(guangzhou);
guangdong.add(shenzhen);
AddressComponent tianhe = new District("tianhe");
AddressComponent yuexiu = new District("yuexiu");
guangzhou.add(tianhe);
guangzhou.add(yuexiu);
AddressComponent longhua = new District("longhua");
AddressComponent baoan = new District("baoan");
shenzhen.add(longhua);
shenzhen.add(baoan);
guangdong.print();