组合模式

定义

  • 将对象组合成树形结构以表示 "部分-整体" 的层次结构。
  • 组合模式使客户端对对单个对象和组合对象保持一致的方式处理。
  • 组合模式用来表示部分与整体的层次结构(类似于树结构),而且也可以使用户对单个对象(叶子节点)以及组合对象(非叶子节点)的使用具有一致性,一致性的意思就是说,这些对象都拥有相同的接口。
  • 如文件系统,书的目录,网站的菜单都可以使用组合模式。

简单来说,组合模式能让我们能用树形的方式创建对象结构,树中包含了组合对象(如文件夹)以及单一对象(文件夹下的文件),我们能使用相同的操作应用在组合和单一对象上,可以忽略组合和单一对象的差异性。

类型

结构型

适用场景

  • 希望客户端可以忽略组合对象和单一对象时
  • 处理一个树形结构时

优点

  • 可以清楚的定义分层次的复杂对象,表示对象的全部或部分整体;
  • 让客户端忽略了层次的差异,方便对整个层次结构进行控制;
  • 简化客户端代码;
  • 符合开闭原则;

缺点

  • 限制类型时会较为复杂
  • 使设计便得抽象

UML类图

组合模式又分为透明模式和安全模式。

透明模式是把组合使用的方法放到抽象类中,不管叶子对象还是树枝对象都有相同的结构,这样做的好处就是叶子节点和树枝节点对于外界没有区别,它们具备完全一致的行为接口。

但同样叶子节点的有些方法可能只是空实现了接口,所以实现它是没有意义的。UML结构图如下:

在这里,leaf是单一对象,composite就是组合对象,通过组合模式,它们都实现了相同的方法,虽然在leaf中add和remove方法是没有意义的。

安全模式是把树枝节点和树叶节点彻底分开,树枝节点单独拥有用来组合的方法,这种方法比较安全。但由于不够透明,所以树叶节点和树枝节点将不具有相同的接口或方法,客户端的调用需要做相应的判断,带来了不便。UML结构图如下:

 

代码Demo

业务场景:文件夹,文件夹下又多个文件

  • FileComponent
public interface FileComponent {

    void add(FileComponent component);
    void remove(FileComponent component);
    void print();

}
  • File(Leaf)
public class File implements FileComponent {
    private String name;
    public File(String name) {
        this.name = name;
    }

    @Override
    public void add(FileComponent component) {}
    @Override
    public void remove(FileComponent component) {}
    @Override
    public void print() {
        System.out.println(name);
    }
}
  • Folder(Composite)
public class Folder implements FileComponent {
    private ArrayList<FileComponent> arrayList = new ArrayList<>();

    @Override
    public void add(FileComponent component) {
        arrayList.add(component);
    }

    @Override
    public void remove(FileComponent component) {
        arrayList.remove(component);
    }

    @Override
    public void print() {
        System.out.println("文件夹");
        for (FileComponent fileComponent:arrayList){
            System.out.print("    ");
            fileComponent.print();
        }
    }
}
  • 应用层
public class Test {
    public static void main(String[] args) {
        FileComponent folder = new Folder();
        folder.add(new File("文件1"));
        folder.add(new File("文件2"));
        folder.add(new File("文件3"));

        folder.print();
    }
}

 

源码中的组合模式

ArrayList类中的addAll方法就采用了组合模式

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable

public interface List<E> extends Collection<E>
    public boolean addAll(Collection<? extends E> c) {
        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  // Increments modCount
        System.arraycopy(a, 0, elementData, size, numNew);
        size += numNew;
        return numNew != 0;
    }

ArrayList实现了List接口,而List接口继承了Collection接口,所以在这里正式组合模式的应用。

HashMap类中的putAll也和上面的方法相同

public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable {
public void putAll(Map<? extends K, ? extends V> m) {
        putMapEntries(m, true);
    }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值