设计模式之组合模式


1.定义

组合模式属于结构型模式,也叫整体-部分模式,目的是将单个对象和组合对象使用相同的接口进行表示,使得客户端对单个对象和组合对象的使用具有一致性。
可以将对象类之间的引用看做一棵树,组合对象(容器对象)就是树干,而单个对象就是树的叶子节点。
通过对两者使用相同的抽象方式,在客户端看来,可以当做一种对象进行处理,不需要关心内部具体是什么,而对于树而言,无论是整体还是部分,都具有相同的操作方式。

可以类比操作系统的文件系统,所有的东西被抽象成了文件,无论是目录、文件、块设备,进程信息等等,通过统一的描述,使得不同的设备可以向外提供统一的接口。

在这里插入图片描述

如果按照传统的方式去描述这些对象,那不得不为每一种类型建立一种抽象,客户端不得不维护多种类型的对象,一个文件夹中可能包含另外的文件夹,也可能包含文件,甚至一种设备信息,如果将他们的公共部分进行抽象,各个类型只需要完成他们各自的行为即可。

通过公共的抽象,对客户端屏蔽了各个类型层次的差异,将客户端和对象解耦,并且可以任意的增加容器对象和叶子对象,符合开闭原则。

因为将所有层次的对象进行统一的抽象,看上去有些违反了单一职责原则和接口隔离,但是设计模式本身就不是强行套用概念,不同的设计模式都是为了更好的解决一类问题,组合模式适合处理一些具有统一行为的树形结构对象,简化客户单的维护成本

其中包含以下几种角色

  • Component(抽象构件):一般是抽象类,为容器对象和叶子对象进行统一的抽象描述,该角色包含了所有子类共有的行为
  • Leaf(叶子构件):表示没有子节点的对象,实现了抽象构件中叶子节点应有的行为
  • Composite(容器构件):容器对象,通过内部维护一个集合,包含了它的所有子节点,子节点可以是叶子对象或者容器对象,容器对象实现了容器对象应有的行为

在这里插入图片描述
图片来源:百度图片

2.示例

组合模式本质上看就是一个不规则的树一直向下延伸,直到叶子节点,关键点是将树的所有不同层次的节点进行抽象
以文件夹和文件做一个简单的实现,定义一个目录和文件的公共抽象类,因为父类职责并不单一,所以所有方法默认抛出异常,不允许调用父类

/**
 * @description: 抽象构件component
 * @version: 1.0
 */
public abstract class AbstractFile {

    private String name;

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

    public void addFile(AbstractFile file){
        throw new UnsupportedOperationException();
    }

    public void removeFile(AbstractFile file){
        throw new UnsupportedOperationException();
    }

    public AbstractFile getFile(int i){
        throw new UnsupportedOperationException();
    }

	//打印所有文件
    public void list(){
        throw new UnsupportedOperationException();
    }
    
    //打印树结构
    public void tree(AbstractFile pNode,int grade){
        throw new UnsupportedOperationException();
    }

}

定义叶子构件,实现抽象构件,因为是文件类型,只实现自身的行为,比如list和tree

/**
 * @description: 容器构件
 * @version: 1.0
 */
public class File extends AbstractFile{

    private String name;

    public File(String name) {
        super(name);
        this.name = name;
    }

    @Override
    public void list() {
        System.out.println(name);
    }
    public void tree(AbstractFile pNode,int grade) {
        String n = "";
        for (int i = grade; i >=0; i--){
            n += "   ";
        }
        System.out.println(n + "+——" + name);
    }

}

最后是容器构件,容器构件应维护一个子节点构件集合,既可以是文件也可以是文件夹

/**
 * @description: 容器构件
 * @version: 1.0
 */
public class Folder extends AbstractFile{

    private String name;

    private List<AbstractFile> abstractFiles = new ArrayList<>();

    public Folder(String name) {
        super(name);
        this.name = name;
    }

    @Override
    public void addFile(AbstractFile file) {
        abstractFiles.add(file);
    }

    @Override
    public void removeFile(AbstractFile file) {
        abstractFiles.remove(file);
    }

    @Override
    public AbstractFile getFile(int i) {
        return abstractFiles.get(i);
    }

    @Override
    public void list() {
        for (Iterator<AbstractFile> it = abstractFiles.iterator();it.hasNext();){
            AbstractFile next = it.next();
            next.list();
        }
    }
     @Override
    public void tree(AbstractFile pNode,int grade) {
        String n = "";
        for (int i = grade; i >=0; i--){
            n += "   ";
        }
        System.out.println(n + "+——" + name);
        ++grade;
        for (Iterator<AbstractFile> it = abstractFiles.iterator();it.hasNext();){
            AbstractFile next = it.next();
            next.tree(this,grade);
        }
    }

}

测试

/**
 * @description: test
 * @version: 1.0
 */
public class Test {
    public static void main(String[] args) {
        AbstractFile f0 = new File("f0");
        AbstractFile f1 = new File("f1");
        AbstractFile f2 = new File("f2");
        AbstractFile f3 = new File("f3");
        AbstractFile f4 = new File("f4");
        AbstractFile f5 = new File("f5");
        AbstractFile f6 = new File("f6");
        AbstractFile f7 = new File("f7");
        AbstractFile f8 = new File("f8");
        AbstractFile f9 = new File("f9");

        AbstractFile dir0 = new Folder("dir0");
        AbstractFile dir1 = new Folder("dir1");
        AbstractFile dir2 = new Folder("dir2");
        //将f0-f4添加到目录0中
        dir0.addFile(f0);
        dir0.addFile(f1);
        dir0.addFile(f2);
        dir0.addFile(f3);
        dir0.addFile(f4);

        //将f5 f6添加到目录1中
        dir1.addFile(f5);
        dir1.addFile(f6);

        //将f7 f7 f8添加到目录2中
        dir2.addFile(f7);
        dir2.addFile(f8);
        dir2.addFile(f9);

        //将目录2添加到目录1
        dir1.addFile(dir2);
        //再将目录1添加到目录0
        dir0.addFile(dir1);
        
        //dir0.list();
        dir0.tree(dir0,0);
    }
}

input:

   +——dir0
      +——f0
      +——f1
      +——f2
      +——f3
      +——f4
      +——dir1
         +——f5
         +——f6
         +——dir2
            +——f7
            +——f8
            +——f9
3.总结

组合模式适用打的场景,在具有整体和部分层次结构中,希望通过一种方式忽略整体和部分的差异,允许客户端一致的对待它们,或者系统中需要一个树结构,能够分离出叶子对象和容器对象,类型不固定,并且允许扩展。

文中部分内容参考自:博客

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值