本篇讲解Java设计模式中的组合模式,分为定义、模式应用前案例、结构、模式应用后案例、适用场景、模式可能存在的困惑和本质探讨7个部分。
定义
组合模式是将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得客户对单个对象和复合对象的使用具有一致性。
在新的分类方式中,组合模式被划分至类之间的交互类别中,其简化的是调用方与具备树结构的一组对象之间的交互,具体通过一致性的行为实现。
模式应用前案例
下面以一个典型的文件和目录为例来进行说明,先来看一下未应用组合模式之前的代码实现。
public class File {//文件结构
private final String name;
public File(String name) {
this.name = name;
}
public void display() {
System.out.println("File: " + this.name);
}
}
public class Directory {//目录结构
private String name;
private final List<File> files;
private final List<Directory> directories;
// 初始化方法
public Directory(String name){
this.name = this.name;
this.files = new ArrayList<>();
this.directories = new ArrayList<>();
}
// 添加子节点
public void addFile(File file){
this.files.add(file);
}
// 添加子目录
public void addDirectory(Directory directory) {
this.directories.add(directory);
}
public void display(){
//System.out.println("Directory:"+this.name);
for(File file : this.files){
file.display();
}
for (Directory dir : this.directories) {
dir.display();
}
}
}
public class Client {//调用方代码
public static void main(String[] ars){
Directory root= new Directory("Root");
File file1=new File("file1.txt");
File file2=new File("file2.txt");
root.addFile(file1);
root.addFile(file2);
Directory subDirecory =new Directory ("Subdirectory");
File file3 = new File("file3.tx");
File file4 = new File("file4.tx");
subDirecory.addFile(file3);
subDirecory.addFile(file4);
root.addDirectory(subDirecory);
root.display();
}
}
我们知道,文件和目录两者是一个大的树结构中的节点。在上面未使用组合模式的代码中,文件和目录都有自己定义的方法。这样在构建一个多层树结构的过程中,复杂度会提升。
结构
组合模式的示例代码如下。
public interface Component {
void operation();
void add(Component component);
void remove(Component component);
Component Display(int index);
}
public class Leaf implements Component{
private String name;
public Leaf(String name) {
this.name = name;
}
@Override
public void operation() {
System.out.println("Leaf: " + name + " operation()");
}
@Override
public void add(Component component) {
throw new UnsupportedOperationException("Leaf cannot have children");
}
@Override
public void remove(Component component) {
throw new UnsupportedOperationException("Leaf cannot remove children");
}
@Override
public Component Display(int index) {
throw new UnsupportedOperationException("Leaf cannot get child");
}
}
public interface Component {
void operation();
void add(Component component);
void remove(Component component);
Component Display(int index);
}
public class Client {
public static void main(String[] args) {
// 创建叶子节点
Component leaf1 = new Leaf("LeafA");
Component leaf2 = new Leaf("LeafB");
Component leaf3 = new Leaf("LeafC");
// 创建复合节点
Component composite = new Composite("CompositeX");
composite.add(leaf1);
composite.add(leaf2);
// 创建另一个复合节点,并添加之前的复合节点和新的叶子节点
Component root = new Composite("Root");
root.add(composite);
root.add(leaf3);
// 执行操作
root.operation();
}
}
模式应用后案例
上面文件与目录的案例,使用组合模式之后的代码实现如下。
public interface IComponent {//接口
void display();
}
public class File implements IComponent{//文件实现
private final String name;
public File(String name) {
this.name = name;
}
@Override
public void display() {
System.out.println("File: " + this.name);
}
}
public class Directory implements IComponent{//目录实现
private String name;
private final List<IComponent> children;
// 初始化方法
public Directory(String name){
this.name = this.name;
this.children = new ArrayList<>();
}
// 添加子节点
public void addComponent(IComponent component){
this.children.add(component);
}
// 显示目录内容
@Override
public void display() {
//System.out.println("Directory: " + this.name);
for (IComponent child : this.children) {
child.display();
}
}
}
public class Client {//调用方代码
public static void main(String[] ars){
Directory root= new Directory("Root");
File file1 = new File("file1.txt");
File file2 = new File ("file2.txt");
root.addComponent(file1);
root.addComponent(file2);
Directory subDirectory =new Directory ("Subdirectory");
File file3 = new File("file3.txt");
File file4 = new File("file4.txt");
subDirectory.addComponent(file3);
subDirectory.addComponent(file4);
root.addComponent(subDirectory);
root.display();
}
}
在上述代码中,由于树的结构使用一个接口和实现的家族来实现,这样树的结构中所有类的行为都是一致的,简化了编码时的复杂度。
适用场景
当需求中出现的一系列概念或对象,它们之间存在部分-整体的层次结构或共同构成一颗树的结构时,就可以考虑使用组合模式。
模式可能存在的困惑
困惑1:组合模式中的“组合”,与“组合优于继承”中的“组合”,有什么关联?
两者都代表了一种关系。前者的“组合”指的是将一系列对象按照层次化结构进行组织。而后者的“组合”指的是两个对象之间的聚合或组合关系,以此来取代类之间继承关系。
本质
组合模式的本质在于提供了一种机制来处理对象之间的部分-整体关系,并且通过统一接口来简化调用方使用复杂层次结构时可能遇到的问题。