组合模式浅谈

136 阅读3分钟

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

一、什么是组合模式

组合模式:组合多个对象形成树形结构,表示整体-部分的关系层次结构。是一种结构型设计模式。常见的例子如部门,部门下面有子部门,比如公司的产品部下面有A产品部,B产品部,与部门是整体与部分的关系。即部门下面包含子部门,两者都是抽象的部门,形成树型结构。此外角色树,也可以使用组合模式进行实现。还有我们的文件结构,也是一种树形结构,也可以用组合模式进行实现。

二、组合模式的实现

(一)、透明组合模式的实现

@Data
@AllArgsConstructor
@NoArgsConstructor
public abstract class AbstractFile {
    private String name;

    public abstract void add(AbstractFile file);

    public abstract void remove(AbstractFile file);

    public abstract List<AbstractFile> getSub();

    public abstract void show(Integer depth);
}

抽象构件,表示文件

public class FileFolder extends AbstractFile {
    private List<AbstractFile> subFile = new ArrayList<>();

    public FileFolder(){super();}

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

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

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

    @Override
    public List<AbstractFile> getSub() {
        return subFile;
    }

    @Override
    public void show(Integer depth) {
        for (int i = 0; i < depth; i++) {
            System.out.print("-");
        }
        System.out.println(super.getName());
        for (AbstractFile file : subFile) {
            file.show(depth + 1);
        }
    }
}

具体构件,子容器,表示文件夹

public class TxtFile extends AbstractFile{

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

    @Override
    public void add(AbstractFile file) {
        throw new RuntimeException("叶子节点无此操作");
    }

    @Override
    public void remove(AbstractFile file) {
        throw new RuntimeException("叶子节点无此操作");
    }

    @Override
    public List<AbstractFile> getSub() {
        throw new RuntimeException("叶子节点无此操作");
    }

    @Override
    public void show(Integer depth) {
        for (int i = 0; i < depth; i++) {
            System.out.print("-");
        }
        System.out.println(super.getName());
    }
}

叶子节点,此处是txt文件。

public static void main(String[] args) {
    AbstractFile file = new FileFolder("根目录");
    AbstractFile firstFile = new TxtFile("根目录下的第一个txt文件");
    file.add(firstFile);
    AbstractFile firstFolder = new FileFolder("根目录下的子目录");
    AbstractFile txtFile = new TxtFile("子目录下的文件");
    firstFolder.add(txtFile);
    file.add(firstFolder);
    file.show(1);
}

组合模式符合开闭原则,如果有其它类型的叶子节点,直接创建新的叶子节点类即可,比如新增doc类型的文件时,创建相应的叶子节点就完成了文件类型的新增。

image.png

(二)、安全组合模式的实现

@Data
@AllArgsConstructor
@NoArgsConstructor
public abstract class AbstractFile002 {
    private String name;

    public abstract void show(Integer depth);
}

抽象构件,文件夹,没有操作子构件的相关方法的定义。在具体的构件中,进行实现。

public class FileFolder002 extends AbstractFile002 {
    private List<AbstractFile002> subFile = new ArrayList<>();

    public FileFolder002() {
        super();
    }

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

    public void add(AbstractFile002 file) {
        subFile.add(file);
    }

    public void remove(AbstractFile002 file) {
        subFile.remove(file);
    }

    public List<AbstractFile002> getSub() {
        return subFile;
    }

    @Override
    public void show(Integer depth) {
        for (int i = 0; i < depth; i++) {
            System.out.print("-");
        }
        System.out.println(super.getName());
        for (AbstractFile002 file : subFile) {
            file.show(depth + 1);
        }
    }
}

文件夹,增加了管理子构件的方法。

public class TxtFile002 extends AbstractFile002 {
    public TxtFile002() {
        super();
    }


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


    @Override
    public void show(Integer depth) {
        for (int i = 0; i < depth; i++) {
            System.out.print("-");
        }
        System.out.println(super.getName());
    }
}

txt文件,叶子节点。

public static void main(String[] args) {
    FileFolder002 file = new FileFolder002("根目录");
    AbstractFile002 firstFile = new TxtFile002("根目录下的第一个txt文件");
    file.add(firstFile);
    FileFolder002 firstFolder = new FileFolder002("根目录下的子目录");
    AbstractFile002 txtFile = new TxtFile002("子目录下的文件");
    firstFolder.add(txtFile);
    file.add(firstFolder);
    file.show(1);
}

可以看到安全组合模式,叶子节点无法调用管理子构件的方法,减少了在运行时出现问题的概率。相对的,操作的时候就需要对具体的子构件进行具体的操作。

image.png

三、总结

组合模式的三种角色:

  1. component:抽象构件容器,为子节点和和子容器声明接口(包括新增子构件,删除子构件,获取子构件以及相关的操作方法),component可以是接口或者抽象类。
  2. leaf:叶子构件,抽象构件的实现。如果在文件管理系统中,指的是具体的某种文件,比如txt文件,没有子节点。没有新增子构件和删除子构件以及获取子构件等方法或调用时报错,或者其它处理方式。
  3. composite:子容器构件。提供了一个集合保存子节点(保存抽象构件)。在文件系统中表示文件夹。

组合模式有透明组合模式和安全组合模式的区别,两者在实现上的区别是透明组合模式的抽象构件中定义了管理子构件的方法,而安全组合模式中没有定义。

  1. 透明组合模式:在抽象构件中声明了新增构件,删除构件,获取子构件等方法,所有的构件都具有相同的方法,透明组合模式是不安全的,因为叶子节点也实现了管理构件的方法,在不区分构件类型的情况下调用这些方法可能会出错。
  2. 安全组合模式:安全组合模式的缺点是不透明,因为叶子节点和子容器具有不同的方法,因此客户端不能完全针对抽象编程,需要有意识的区分叶子节点和子容器。可以看到在上面的安全模式的实现中,创建子容器节点时,我把节点声明成了子容器,然后才可以调用管理节点的方法。

如有问题,请大佬指正。