首页 > 其他分享 >掌握设计模式--组合模式

掌握设计模式--组合模式

时间:2025-01-03 16:45:43浏览次数:1  
标签:组合 -- HTMLElement new div 设计模式 public

组合模式(Composite Pattern)

组合模式(Composite Pattern)是一种结构型设计模式,它用于将对象组织成树形结构,以表示部分-整体的层次结构。通过组合模式,客户端可以统一对待单个对象和组合对象,从而简化了客户端代码的复杂性。

组合模式的核心思想

  1. 统一的接口:通过抽象类或接口将单个对象组合对象统一起来;
  2. 递归组合组合对象中可以包含单个对象或其他组合对象;
  3. 透明性:客户端可以一致地调用单个对象和组合对象的方法,而无需区分两者的差异。

组合模式的角色

  1. 组件(Component)
    定义单个对象和组合对象的公共接口,例如通用操作(addremovegetChild等)。

  2. 叶子节点(Leaf)
    表示树形结构中的基本单元,不能再包含其他对象。它实现了组件接口,但不支持添加或移除操作。

  3. 组合对象(Composite)
    表示树形结构中的复杂单元,可以包含叶子节点或其他组合对象。它实现组件接口,并负责管理其子对象的操作。

示例代码

组合模式解析XML或 HTML 元素结构的代码示例。我们将 XML/HTML 元素看作“部分-整体”结构,其中:

  • 叶子节点(leaf):表示没有子节点的元素(如 <img><input>)。
  • 组合节点(Composite):表示可以包含其他子元素的元素(如 <div><body>)。

两种节点使用同一种的顶层抽象,属于同一类对象,统称为元素(节点)。

类图

image

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>
    <div>
      <img />
      <p />
    </div>
    <input />
  </body>
</html>
<html>
  <body>
    <div>
      <img />
    </div>
    <input />
  </body>
</html>

从类图或测试类(使用者)中可以看出,使用者直接依赖于具体的类,属于高耦合的一种编程方式。

简单优化(结合其它设计模式)

加入一个工厂类来创建组合节点和叶子结点

类图结构变为

image

工厂类代码

public class HTMLElementFactory {
    private static Map<String, Class<? extends HTMLElement>> elementRegistry = new HashMap<>();

    static {
        // 注册类
        try {
            HTMLElementFactory.registerElement("composite", (Class<? extends HTMLElement>) Class.forName("org.example.composite.htmldemo.HTMLComposite"));
            HTMLElementFactory.registerElement("leaf", (Class<? extends HTMLElement>) Class.forName("org.example.composite.htmldemo.HTMLLeaf"));
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }

    // 注册类
    public static void registerElement(String type, Class<? extends HTMLElement> clazz) {
        elementRegistry.put(type, clazz);
    }

    // 创建实例
    public HTMLElement createElement(String type, String name) {
        Class<? extends HTMLElement> clazz = elementRegistry.get(type);
        if (clazz == null) {
            throw new IllegalArgumentException("未知元素类型: " + type);
        }
        try {
            return clazz.getDeclaredConstructor(String.class).newInstance(name);
        } catch (Exception e) {
            throw new RuntimeException("错误创建元素对象: " + e.getMessage(), e);
        }
    }
}

测试代码

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");

        HTMLElementFactory htmlElementFactory = new HTMLElementFactory();
        // 创建对象
        HTMLElement html = htmlElementFactory.createElement("composite", "html");
        HTMLElement body = htmlElementFactory.createElement("composite", "body");
        HTMLElement div = htmlElementFactory.createElement("composite", "div");
        HTMLElement input = htmlElementFactory.createElement("leaf", "input");
        HTMLElement img = htmlElementFactory.createElement("leaf", "img");
        HTMLElement p = htmlElementFactory.createElement("leaf", "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);
    }
}

测试结果和前面的一样。

除了结合工厂模式外,还可以结合其它设计模式。比如,结合迭代器模式来递归遍历组合树。

总结

组合模式(Composite Pattern)是一种结构型设计模式,它用于将对象组织成树形结构,以表示部分-整体的层次结构。同时可以结合其它设计模式,使组合模式变得更加灵活和高效。

优点

  1. 透明性:客户端无需区分单个对象和组合对象。
  2. 灵活性:可以方便地动态组合对象,拓展系统。
  3. 符合开闭原则:添加新的组件类无需修改现有代码。

开闭原则的核心是:

  • 对扩展开放:可以通过扩展系统的功能,而不是修改已有的代码来实现新需求。
  • 对修改关闭:已有的代码逻辑不需要因需求变化而被改动。

关于开闭原则的“修改关闭”主要是指不修改核心业务逻辑。对于工厂类这样的“管理型”代码,适当的修改是可以接受的,因为它并不属于核心业务逻辑。

缺点

  1. 复杂性增加:系统中的类和对象数量可能会增多。
  2. 单一职责原则的可能破坏:组合对象需要管理其子对象,可能职责过多。

使用场景

  1. 文件系统(文件和文件夹);
  2. GUI(窗口、按钮、文本框等控件);
  3. 公司组织架构(员工与部门);
  4. XML或HTML的元素结构。

image

什么是设计模式?

单例模式及其思想

设计模式--原型模式及其编程思想

掌握设计模式之生成器模式

掌握设计模式之简单工厂模式

掌握设计模式之工厂方法模式

掌握设计模式--装饰模式


超实用的SpringAOP实战之日志记录

2023年下半年软考考试重磅消息

通过软考后却领取不到实体证书?

计算机算法设计与分析(第5版)

Java全栈学习路线、学习资源和面试题一条龙

软考证书=职称证书?

软考中级--软件设计师毫无保留的备考分享

标签:组合,--,HTMLElement,new,div,设计模式,public
From: https://www.cnblogs.com/dennyLee2025/p/18650441

相关文章

  • 视频号直播自动回复浏览器插件,帮我自动回评论,也可以不停的循环发评论 vx: llike620
    开启直播以后,一定要在视频号助手后台,有直播管理页面下,就是那个展示评论和能发送评论框的页面,启动插件。要把自己当前发评论的昵称屏蔽掉,否则会捕获到自己回复的,造成死循环。视频号直播在回复时,会自动点击这条评论,然后再点击回复按钮,那么在用户那边看就是单独回复给我的。并且因......
  • JavaMock批量生成GET SET方法单测覆盖率
    @RunWith(MockitoJUnitRunner.class)@Slf4jpublicclassPersonTest{@TestpublicvoidtestPerson(){MockGetSetMethod(Person.class);}/***@paramtClass需要生成覆盖率的实体类*@param<T>泛型*/public<T>......
  • 数据库开发规范v1.0_.241127
    一、建表规约【强制】表达是与否概念的字段,必须使用is_xxx的方式命名,数据类型是unsignedtinyint(1表示是,0表示否)。说明:任何字段如果为非负数,必须是unsigned。正例:表达逻辑删除的字段名deleted_flag,0表示删除,1表示未删除。【强制】表名、字段名必须使用小写字母或......
  • vue3 在渲染md中的数学公式
    常规的md转数学公式插件无法解决此问题问题:在渲染过程中\t被转义导致渲染出错**方案为:将\t转义为\t**依赖的插件及版本"katex":"^0.16.15","markdown-it":"^14.1.0","markdown-it-katex":"^2.0.3","markdown-it-latex&......
  • 重新定义电商团队协作:在线文档工具的战略作用
    在当今快速发展的电商行业,团队协作效率对业务成功至关重要。尤其是跨部门沟通和信息共享,已经成为提升电商团队组织架构优化的关键因素。而一个功能强大的在线协同编辑文档工具正是提升这些关键环节的解决方案之一。电商团队组织架构的挑战电商企业通常面临复杂的业务流程和多层......
  • SQL Server创建用户一直提示用户已存在的解决办法.241217
    背景:复制的老数据库,创建账号onlyread时,一直提示数据库里有这个用户名。报错如下:“用户、组或角色'onlyread'在当前数据库中已存在。”解决方法:1.查询数据库,是否有这个用户--查询是否存在指定的用户、组或者角色SELECT*FROMsys.database_principalsWHEREname='only......
  • 在线CAD绘制门和窗(WEB CAD二次开发家装设计软件)
    一、前言Mxcad是使用TypeScript、C++语言开发的一个网页CAD底层平台,它为用户提供了丰富的开发接口,此框架功能丰富、使用简易高效,可帮助大家在网页二开与自己专业相关的网页CAD应用。我们以家装行业为例,介绍mxcad如何快速实现墙体、单开门、标准窗等实体,并实现这些实体之间的联动......
  • 数据分层 ODS DW DM层级.241203
    在数据仓库的设计过程中,数据分层是一种重要的组织方式,能够提高数据处理效率和数据质量。数据分层通常包括原始数据(OperationalDataStore,ODS)、明细数据(DataWarehouse,DW)和汇总数据(DataMart,DM)三个层级。下面将详细介绍这三个层级的作用以及如何优雅地设计它们。一、原始数据层(OD......
  • OpenEuler文件被锁定的解决方法网卡修改不生效的解决办法.241202
    欧拉系统(含centos等linux系统)修改文件,一直提示readonly,不让改。原因有可能是这个文件给锁定了。解决方法:使用以下两个命令:•chattr改变文件属性•lsattr文件查看文件属性例如:•chattr+i将文件锁住,任何用户都不能进行修改•chattr+a只能向文件追加数据,不能删除......
  • 星链StarLink二代终端拆解
    星链StarLink二代终端拆解    Starlink 天线的技术比目前战斗机上的技术更先进。2020年12月3日,美国专利&商标局公布了SpaceXStarlink专利文件合集。精心制作的文件最初于2020年6月提交给专利代理机构。每份文件都展示了解释卫星网络如何与相控阵碟形天线一......