组合模式
定义
- 将对象组合成树形结构以表示
“部分-整体”
的层次结构- Composite 使得用户对单个对象和组合对象的使用具有一致性(稳定)
其实根节点和树枝节点本质上属于同一种数据类型,可以作为容器使用,而叶子节点与树枝节点在语义上不属于用一种类型。但是在组合模式中,会把树枝节点和叶子节点看作属于同一种数据类型(用统一接口定义)让它们具备一致行为,将对象组合成树形结构以表示 “部分-整体” 的层次结构。在组合模式中,整个树形结构中的对象都属于同一种类型,带来的好处就是用户不需要辨别是树枝节点还是叶子节点,可以直接进行操作,给用户的使用带来极大的便利。
使用场景
- 希望客户端可以忽略组合对象与单个对象的差异时
- 处理一个树形结构时
角色
抽象构件(Component)角色
它的主要作用是为树叶构件和树枝构件声明公共接口,并实现它们的默认行为
树枝构件(Composite)角色
- 是组合中的分支节点对象,它有子节点。它实现了抽象构件角色中声明的接口,它的主要作用是存储和管理子部件
- 通常包含 Add()、Remove()、GetChild() 等方法
树叶构件(Leaf)角色
是组合中的叶节点对象,它没有子节点,用于实现抽象构件角色中声明的公共接口
代码实现
MenuComponent.java
/**
* 抽象根节点
*
* @author BNTang
* @date 2021/10/12
*/
@Data
public abstract class MenuComponent {
protected String name;
protected int level;
/**
* 添加子菜单
* 可以添加菜单,可以添加菜单项,声明为父类型
*
* @param menuComponent 菜单组件
*/
public void add(MenuComponent menuComponent) {
// 如果是菜单项,下面不能再有子菜单, 菜单项不覆盖此方法
// 如果调用的话, 就会报不支持此操作
throw new UnsupportedOperationException();
}
/**
* 移除子菜单 和添加菜单同理
*
* @param menuComponent 菜单组件
*/
public void remove(MenuComponent menuComponent) {
throw new UnsupportedOperationException();
}
/**
* 获取指定菜单
*
* @param index 索引
* @return {@link MenuComponent}
*/
public MenuComponent getChild(int index) {
throw new UnsupportedOperationException();
}
/**
* 打印菜单名称(包含子菜单和子菜单项)
* 对于菜单和菜单项 他们的实现方法是不一样.
* 定义一个抽象方法,让子类去实现
*/
public abstract void print();
}
Menu.java
/**
* 菜单类 树枝节点
*
* @author BNTang
* @date 2021/10/12
*/
public class Menu extends MenuComponent {
/**
* 菜单可以有多个子菜单或者子菜单间项
*/
private final List<MenuComponent> menuComponentList = new ArrayList<>();
Menu(String name, int level) {
this.name = name;
this.level = level;
}
@Override
public void add(MenuComponent menuComponent) {
menuComponentList.add(menuComponent);
}
@Override
public void remove(MenuComponent menuComponent) {
menuComponentList.remove(menuComponent);
}
@Override
public MenuComponent getChild(int index) {
return menuComponentList.get(index);
}
@Override
public void print() {
IntStream.range(0, level).forEach(i -> System.out.print("--"));
// 打印菜单名称
System.out.println(name);
menuComponentList.forEach(MenuComponent::print);
}
}
MenuItem.java
/**
* 菜单项,属于叶子节点
*
* @author BNTang
* @date 2021/10/12
*/
public class MenuItem extends MenuComponent {
public MenuItem(String name, int level) {
this.name = name;
this.level = level;
}
@Override
public void print() {
IntStream.range(0, level).forEach(i -> System.out.print("--"));
System.out.println(name);
}
}
Client.java
/**
* 客户端
*
* @author BNTang
* @date 2021/10/12
*/
public class Client {
public static void main(String[] args) {
MenuComponent menu1 = new Menu("菜单管理", 2);
menu1.add(new MenuItem("添加菜单", 3));
menu1.add(new MenuItem("编辑菜单", 3));
MenuComponent menu2 = new Menu("权限管理", 2);
menu2.add(new MenuItem("删除权限", 3));
menu2.add(new MenuItem("更新权限", 3));
MenuComponent menu3 = new Menu("角色管理", 2);
menu3.add(new MenuItem("更新角色", 3));
menu3.add(new MenuItem("添加角色", 3));
MenuComponent rootMenu = new Menu("系统管理", 1);
rootMenu.add(menu1);
rootMenu.add(menu2);
rootMenu.add(menu3);
rootMenu.print();
}
}
UML 图
优点
- 清楚地定义分层次的复杂对象,表示对象的全部或部分层次
- 让客户端忽略了层次的差异,方便对整个层次结构进行控制
- 简化客户端代码
源码应用
JDK:Container
如上是 java.awt
做图形化界面的这个类。
myBatis:SqlNode
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具