组合模式(Composite Pattern)
组合模式(Composite Pattern)是一种结构型设计模式,它用于将对象组织成树形结构,以表示部分-整体的层次结构。通过组合模式,客户端可以统一对待单个对象和组合对象,从而简化了客户端代码的复杂性。
组合模式的核心思想
- 统一的接口:通过抽象类或接口将单个对象和组合对象统一起来;
- 递归组合:组合对象中可以包含单个对象或其他组合对象;
- 透明性:客户端可以一致地调用单个对象和组合对象的方法,而无需区分两者的差异。
组合模式的角色
- 组件(Component)
定义单个对象和组合对象的公共接口,例如通用操作(add、remove、getChild等)。
- 叶子节点(Leaf)
表示树形结构中的基本单元,不能再包含其他对象。它实现了组件接口,但不支持添加或移除操作。
- 组合对象(Composite)
表示树形结构中的复杂单元,可以包含叶子节点或其他组合对象。它实现组件接口,并负责管理其子对象的操作。
示例代码
组合模式解析XML或 HTML 元素结构的代码示例。我们将 XML/HTML 元素看作“部分-整体”结构,其中:
- 叶子节点(leaf):表示没有子节点的元素(如 <img> 或 )。
- 组合节点(Composite):表示可以包含其他子元素的元素(如 或 )。
两种节点使用同一种的顶层抽象,属于同一类对象,统称为元素(节点)。
类图
1. 抽象组件
HTML顶层元素抽象类。你也可以定义一个顶层接口,然后在抽象类中实现基础功能。- public abstract class HTMLElement {
- protected String name;
- public HTMLElement(String name) {
- this.name = name;
- }
- public abstract void render(int level);
- public HTMLElement getChild(int index) {
- throw new UnsupportedOperationException();
- }
- // 默认行为:叶子结点禁止新增元素
- public void addChild(HTMLElement element) {
- throw new UnsupportedOperationException();
- }
- // 默认行为:叶子结点禁止移除子元素
- public void removeChild(HTMLElement element) {
- throw new UnsupportedOperationException();
- }
- // 辅助方法:生成缩进
- protected String generateIndent(int level) {
- StringBuilder indent = new StringBuilder();
- for (int i = 0; i < level * 2; i++) {
- indent.append(" "); // 每层缩进2个空格
- }
- return indent.toString();
- }
- }
复制代码 2. 组合结点
表示可以包含子元素的HTML标签- public class HTMLComposite extends HTMLElement {
- private List<HTMLElement> children = new LinkedList<>();
- public HTMLComposite(String name) {
- super(name);
- }
- @Override
- public void addChild(HTMLElement element) {
- children.add(element);
- }
- @Override
- public void removeChild(HTMLElement element) {
- children.remove(element);
- }
- @Override
- public HTMLElement getChild(int index) {
- return children.get(index);
- }
- @Override
- public void render(int level) {
- System.out.println(generateIndent(level) + "<" + name + ">");
- for (HTMLElement child : children) {
- child.render(level + 1); // 子节点递归调用
- }
- System.out.println(generateIndent(level) + "</" + name + ">");
- }
- }
复制代码 3.叶子节点
表示没有子元素的HTML标签- public class HTMLLeaf extends HTMLElement {
- public HTMLLeaf(String name) {
- super(name);
- }
- @Override
- public void render(int level) {
- System.out.println(generateIndent(level) + "<" + name + " />");
- }
- }
复制代码 测试
- public class CompositePatternHTMLDemo {
- public static void main(String[] args) {
- // 创建HTML结构
- HTMLElement html = new HTMLComposite("html");
- HTMLElement body = new HTMLComposite("body");
- HTMLElement div = new HTMLComposite("div");
- HTMLElement img = new HTMLLeaf("img");
- HTMLElement input = new HTMLLeaf("input");
- HTMLElement p = new HTMLLeaf("p");
- // 组合结构
- html.addChild(body);
- body.addChild(div);
- body.addChild(input);
- div.addChild(img);
- div.addChild(p);
- // 渲染HTML结构
- html.render(0);
- // 去除某个节点
- div.removeChild(p);
- html.render(0);
- }
- }
复制代码 测试结果:- <html>
- <body>
-
- <img />
- <p />
-
- <input />
- </body>
- </html>
- <html>
- <body>
-
- <img />
-
- <input />
- </body>
- </html>
复制代码 从类图或测试类(使用者)中可以看出,使用者直接依赖于具体的类,属于高耦合的一种编程方式。
简单优化(结合其它设计模式)
加入一个工厂类来创建组合节点和叶子结点
类图结构变为
工厂类代码
[code]public class HTMLElementFactory { private static Map |