1 组合模式介绍
组合模式是一种结构型设计模式,它允许将对象组合成树形结构,以表示部分-整体的层次结构。组合模式使得客户端可以统一处理单个对象和组合对象,从而简化了客户端代码。
在组合模式中,有两种类型的对象:叶子节点和组合节点。叶子节点表示树形结构中的最底层节点,它们不能再分解为更小的部分。组合节点表示树形结构中的中间节点和根节点,它们可以包含一个或多个子节点,这些子节点可以是叶子节点,也可以是其他的组合节点。
组合模式的核心思想是通过递归方式来遍历整个树形结构,从而实现对整体和部分的操作。客户端可以通过操作组合节点来操作整个树形结构,也可以通过操作叶子节点来操作单个对象。
📌 场景
树形结构:当需要处理一个树形结构的对象集合时,可以使用组合模式。例如操作系统的文件系统,文件夹和文件的嵌套结构。
命令模式:当需要执行一系列操作时,可以使用组合模式将这些操作组合成一个命令对象。例如文本编辑器中的撤销和重做功能。
菜单和导航:当需要创建菜单和导航时,可以使用组合模式。例如网站的导航栏和下拉菜单。
组合对象的属性:当需要对组合对象进行属性设置时,可以使用组合模式。例如在游戏中设计角色的属性,可以将角色的各个部分组合起来形成一个复合对象。
组合对象的操作:当需要对组合对象进行操作时,可以使用组合模式。例如在图形编辑器中,可以对多个图形对象进行组合操作,包括平移、旋转、缩放等。
📌 优缺点
优点:简化客户端代码、统一处理单个对象和组合对象、方便增加新的节点类型、提高代码复用性和可维护性等;
缺点:可能会导致设计过度复杂,不适合处理复杂的逻辑。
2 组合模式实现
以目录和文件为例:
📌 1.定义组件
/**
* 组件
*/
public abstract class Component {
/**
* 添加子组件
* @param component 组件
*/
public abstract void add(Component component);
/**
* 删除子组件
* @param component 组件
*/
public abstract void remove(Component component);
/**
* 获取子组件
* @param index 组件序号
* @return 子组件
*/
public abstract Component getChild(int index);
/**
* 修改名称
*/
public abstract void editName();
}
📌 2.定义目录
/**
* 目录
* <p>目录可以包含多个文件或目录
*/
public class Directory extends Component{
/**
* 使用 List 来存放目录中的子组件
*/
List<Component> child = new ArrayList<>();
@Override
public void add(Component component) {
child.add(component);
}
@Override
public void remove(Component component) {
child.remove(component);
}
@Override
public Component getChild(int index) {
return child.get(index);
}
@Override
public void editName() {
// 继续调用所有子组件的 editName 方法执行业务
child.forEach(Component::editName);
}
}
📌 3.定义文件
/**
* 文件
*/
public class File extends Component{
@Override
public void add(Component component) {
throw new UnsupportedOperationException();
}
@Override
public void remove(Component component) {
throw new UnsupportedOperationException();
}
@Override
public Component getChild(int index) {
throw new UnsupportedOperationException();
}
@Override
public void editName() {
// 具体的名称修改操作
System.out.println("修改文件:" + this);
}
}
📌 4.调用
// 新建一个外层目录
Directory out = new Directory();
// 新建一个内层目录
Directory in = new Directory();
// 外层目录添加文件
out.add(new File());
// 里层目录添加文件
in.add(new File());
in.add(new File());
// 将内层目录放置在外层目录下
out.add(in);
// 修改文件名
out.editName();
控制台输出:
可以看到对最外层目录进行操作后,会递归向下处理当前目录和子目录中所有的文件。