当前位置: 代码迷 >> 综合 >> Java设计模式之组合模式(Composite Pattern)
  详细解决方案

Java设计模式之组合模式(Composite Pattern)

热度:5   发布时间:2023-12-15 17:13:12.0

介绍


组合模式是一种结构型设计模式。它一般是 用来创建树状的结构,表示“部分-整体”的层次关系。由于该模式使用的是对象组合的方式来实现的,区别于继承的方式,因此也叫做合成模式。先来看一下它的定义:

Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly.

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

组合模式主要包括三种角色:

  • Componet抽象构件角色。定义对象的框架,可以在其中实现通用的方法、定义类的属性等等,单个对象和组合对象都要继承它。

  • Leaf叶子组件。相当于树状结构中的叶子,是结构的最下层,下面没有其他组件。

  • Composite树枝组件。相当于树状结构的分枝节点,下面有其他树枝组件或叶子组件,它相当于一个容器,包含下面的各个组件,是其下各个组件的父亲。

有了这三种角色就可以实现组合模式。组合模式的应用有很多,最常见的是我们的文件系统,一个文件夹下面可以有很多其他的文件夹,并且还可以有很多文件,这里的文件夹就相当于我们的树枝组件(容器),而文件就相当于叶子组件(不会包含其他组件)。

组合模式有两种不同的实现:安全模式透明模式。从名字就可以看出来,安全模式比透明模式更安全。其实安全模式是通过 将树枝和树叶组件分开定义 来实现其安全性的,而透明模式是不区分的。下面来具体介绍组合模式。

注:在透明模式的实现中,树枝的方法放到了抽象构件中,因此透明模式实现的组合模式只有两种角色。

安全模式


拿我们的文件系统举例,这里需要二者的抽象构件:

public abstract class FileSystem {protected String fileName = "";public FileSystem(String fileName) {this.fileName = fileName;}
}

然后定义我们的树枝和树叶组件,也就是我们的文件夹和文件:

文件夹:

public class Folder extends FileSystem {
    private List<FileSystem> fileList = new ArrayList<>();public Folder(String fileName) {super(fileName);}//新建文件夹或文件。public void add(FileSystem fileSystem) {this.fileList.add(fileSystem);}//删除文件夹或文件。public void remove(FileSystem fileSystem) {this.fileList.remove(fileSystem);}//获得下面的所有文件、文件夹信息。public List<FileSystem> getChildren() {return this.fileList;}@Overridepublic String toString() {String name = "文件夹名:" + this.fileName + "\n";for (FileSystem fileSystem : fileList) {name = name + fileSystem;}return name;}
}

文件:

public class File extends FileSystem {
    public File(String fileName) {super(fileName);}@Overridepublic String toString() {return "文件名:" + this.fileName + "\n";}
}

最后,三种组件都定义好后,就可以开始我们的测试了:

public static void main(String[] args) {Folder root = new Folder("我的电脑");Folder branch = new Folder("我的图片");File leaf = new File("图片.jpg");root.add(branch);branch.add(leaf);display(root);
}
public static void display(Folder root) {for (FileSystem fileSystem : root.getChildren()) {System.out.println(fileSystem);}
}

输出结果:

文件夹名:我的图片
文件名:图片.jpg

这就是安全模式实现的组合模式,它通过将树枝和树叶分开定义来保证系统的安全性。

透明模式


透明模式是将安全模式中具体类中的方法放到了抽象类中,例如 add() 方法、 remove()方法和 getChildren() 方法等。来看我们更改后的代码:

抽象构件:

public abstract class FileSystem {protected List<FileSystem> fileList = new ArrayList<>();protected String fileName = "";public FileSystem(String fileName) {this.fileName = fileName;}//新建文件夹或文件。public void add(FileSystem fileSystem) {this.fileList.add(fileSystem);}//删除文件夹或文件。public void remove(FileSystem fileSystem) {this.fileList.remove(fileSystem);}//获得下面的所有文件、文件夹信息。public List<FileSystem> getChildren() {return this.fileList;}@Overridepublic String toString() {String name = "文件夹名:" + this.fileName + "\n";for (FileSystem fileSystem : fileList) {name = name + fileSystem;}return name;}
}

文件夹和文件是相同的:

public class File extends FileSystem {
    public File(String fileName) {super(fileName);}//透明模式。@Overridepublic String toString() {String name = "文件夹名:" + this.fileName + "\n";for (FileSystem fileSystem : this.fileList) {name = name + fileSystem;}return name;}
}

最后的测试代码,与安全模式稍有不同:

public static void main(String[] args) {File root = new File("我的电脑");File branch = new File("我的图片");File leaf = new File("图片.jpg");root.add(branch);branch.add(leaf);display(root);
}
public static void display(FileSystem root) {for (FileSystem fileSystem : root.getChildren()) {System.out.println(fileSystem);}
}

输出结果:

文件夹名:我的图片
文件夹名:图片.jpg

可以看到,该模式中的树枝组件和树叶组件结构是一样的,这样就比安全模式少了一个实现类。但这样就会出现一个问题,由于叶子组件是不能使用这些方法的,但是方法放在抽象类中的话,它就可以使用了。这就会造成潜在的危险。虽然叶子组件可以在这些方法可以重写,但并没有什么必要,因此这里还是建议使用安全模式来实现组合模式。

总结


组合模式在创建数据库系统等复杂的对象时,是非常合适的,尤其是树形结构的对象。它的优点有两点:

  • 无论树枝组件还是树叶组件,对调用者来讲是没有任何区别的,高层模块不必关心处理的是组合对象还是单个对象
  • 可以自由增加对象。就像文件系统一样,增加或删除一个文件夹或文件,仅仅只对当前的文件夹有影响,其他部分还是正常工作,这样的开发非常符合开闭原则,扩展十分容易。

然而组合模式也有其缺点,最大的缺点就是使用时直接使用其实现类,这与依赖倒置原则相冲突。

  相关解决方案