不得不说的结构型模式-组合模式

news2024/9/21 14:41:01

组合模式是一种结构型设计模式,它允许您将对象组合成树形结构,并以统一的方式处理它们。该模式基于递归组合的想法,其中一个组件可以由许多更小的组件组成,这些更小的组件可以由更小的组件组成,以此类推。

 

在组合模式中,有两种类型的组件:单个对象和组合对象。单个对象是组成树的最基本的对象,而组合对象则是由多个单个对象和组合对象组成的复杂对象。每个组件都有一个共同的接口,该接口定义了执行操作的方法。组合对象可以递归地调用它们的子组件来执行相同的操作。

组合模式的实际应用非常广泛。它通常用于处理树形结构,例如文件系统、GUI控件、公司组织结构等。在这些应用程序中,组合模式使您能够以递归的方式遍历树并访问所有组件。

组合模式的好处是它可以使代码更简洁,更具可读性。它也可以使您的代码更灵活,更易于扩展。通过将单个对象和组合对象组合成树形结构,您可以轻松地添加、删除或替换组件,而无需对整个系统进行修改。

好处

1. 统一处理组合对象和叶子对象

组合模式中,组合对象和叶子对象被一致对待,都是组件(Component),可以被统一处理。这意味着,我们可以不必区分处理一个叶子对象还是一个组合对象,从而简化了代码的复杂度。

2. 简化客户端代码

由于组合模式可以形成递归结构,因此可以很方便地对整个组合体系进行递归遍历。客户端可以通过一个接口调用整个组合结构,而不必递归遍历每个对象。这样可以大大简化客户端的代码。

3. 增加新的组件类很容易

组合模式的扩展性非常好。当需要增加新的组件类时,只需要扩展Component抽象类,并实现其中的方法即可。其他类都不需要修改,符合“开闭原则”。

此外,组合模式还可以使代码更易于维护。通过使用组合模式,您可以将复杂的树形结构拆分为多个简单的组件,并在需要时对每个组件进行修改。这样可以使代码更易于理解和维护,并且可以使您更容易找到和修复错误。

组合模式的缺点是它可能会导致某些操作的性能下降。由于组合对象包含许多单个对象和组合对象,因此在执行某些操作时可能会产生大量的递归调用,从而导致性能下降。此外,组合模式可能会使代码更加复杂,需要更多的代码来处理树形结构。

缺点

1. 可能过于抽象

组合模式把整个组合体系看成一棵树形结构,这种抽象方式可能导致程序员对实际情况的理解存在偏差。有时候,可能会出现把不应该组合的对象强行组合起来的情况,从而导致系统设计的混乱。

2. 难以限制组合中的组件类型

组合模式的一个缺点是,它难以限制组合中的组件类型。由于组合模式中的Component抽象类并没有定义具体的组件类型,因此我们无法通过类型检查来限制组件类型。如果组合结构中添加了错误的组件类型,运行时会导致错误。

我们以一个组织结构为例,来演示组合模式的实现。假设我们有一个组织结构,由公司、部门和员工组成。其中公司是一个整体,包含多个部门,每个部门又包含多个员工。我们可以使用组合模式来实现该组织结构的管理。

我们首先定义一个抽象类Component,它表示组合中的对象,可以是公司、部门或员工。

class Component {
public:
    virtual void add(Component* c) {}
    virtual void remove(Component* c) {}
    virtual void display(int depth) {}
    virtual ~Component() {}
};

然后我们定义三个具体的类,分别是Company、Department和Employee,它们继承自Component,并实现它们的具体功能。

class Company : public Component {
public:
    Company(std::string name) : m_name(name) {}
    void add(Component* c) override {
        m_components.push_back(c);
    }
    void remove(Component* c) override {
        m_components.erase(std::remove(m_components.begin(), m_components.end(), c), m_components.end());
    }
    void display(int depth) override {
        std::cout << std::string(depth, '-') << m_name << std::endl;
        for (auto& c : m_components) {
            c->display(depth + 2);
        }
    }
private:
    std::string m_name;
    std::vector<Component*> m_components;
};

class Department : public Component {
public:
    Department(std::string name) : m_name(name) {}
    void add(Component* c) override {
        m_components.push_back(c);
    }
    void remove(Component* c) override {
        m_components.erase(std::remove(m_components.begin(), m_components.end(), c), m_components.end());
    }
    void display(int depth) override {
        std::cout << std::string(depth, '-') << m_name << std::endl;
        for (auto& c : m_components) {
            c->display(depth + 2);
        }
    }
private:
    std::string m_name;
    std::vector<Component*> m_components;
};

class Employee : public Component {
public:
    Employee(std::string name) : m_name(name) {}
    void display(int depth) override {
        std::cout << std::string(depth, '-') << m_name << std::endl;
    }
private:
    std::string m_name;
};

最后,我们在客户端代码中使用这些类来构建组织结构。

int main() {
    // 创建公司对象
    Company* company = new Company("ABC Company");
    // 创建部门对象
    Department* department1 = new Department("Sales Department");
    Department* department2 = new Department("Finance Department");
    // 创建员工对象
    Employee* employee1 = new Employee("Alice");
    Employee* employee2 = new Employee("Bob");
    Employee* employee3 = new Employee("Charlie");
    Employee* employee4 = new Employee("David");

    // 将部门和员工添加到公司中
    company->add(department1);
    company->add(department2);
    department1->add(employee1);
    department1->add(employee2);
    department2->add(employee3);
    department2->add(employee4);

    // 显示组织结构
    company->display(0);

    return;
}

再用一个完整的代码示例,展示了如何使用组合模式创建树形结构:

#include <iostream>
#include <vector>
#include <string>

// 抽象组件
class Component {
public:
    virtual ~Component() = default;

    virtual void operation() const = 0;
    virtual void add(Component*) {}
    virtual void remove(Component*) {}
    virtual Component* getChild(int) const { return nullptr; }
};

// 叶子节点
class Leaf : public Component {
public:
    explicit Leaf(std::string name) : name_(std::move(name)) {}

    void operation() const override {
        std::cout << "Leaf " << name_ << " operation.\n";
    }

private:
    std::string name_;
};

// 组合节点
class Composite : public Component {
public:
    explicit Composite(std::string name) : name_(std::move(name)) {}

    void operation() const override {
        std::cout << "Composite " << name_ << " operation:\n";
        for (const auto& child : children_) {
            child->operation();
        }
    }

    void add(Component* component) override {
        children_.push_back(component);
    }

    void remove(Component* component) override {
        children_.erase(std::remove(children_.begin(), children_.end(), component), children_.end());
    }

    Component* getChild(int index) const override {
        if (index >= 0 && index < children_.size()) {
            return children_[index];
        }
        return nullptr;
    }

private:
    std::string name_;
    std::vector<Component*> children_;
};

int main() {
    auto leaf1 = new Leaf("leaf1");
    auto leaf2 = new Leaf("leaf2");
    auto leaf3 = new Leaf("leaf3");
    auto composite1 = new Composite("composite1");
    auto composite2 = new Composite("composite2");

    composite1->add(leaf1);
    composite1->add(leaf2);
    composite1->add(composite2);

    composite2->add(leaf3);

    composite1->operation();

    return 0;
}

在这个例子中,我们创建了一个简单的树形结构,其中 Composite 节点可以包含其他组件,包括其他 Composite 节点和 Leaf 节点。我们可以通过递归调用每个组件的 operation() 方法来执行操作。这个例子展示了组合模式的一个主要好处,即可以轻松地组合对象形成树形结构,而不需要知道这些对象的具体类型,从而提高了代码的灵活性和可扩展性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/448571.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Latex安装与简介

文章目录 Latex一.步入Latex的两种方式:1.下载安装与Vscode集成2.在线的编辑器: overleaf 二.解决中文输入的问题三.简单介绍 Latex 一.步入Latex的两种方式: 1.下载安装与Vscode集成 texlive下载与安装参考: https://blog.csdn.net/weixin_47581344/article/details/1243560…

Midjourney 提示词工具(10 个国内外最好最推荐的)

Midjourney&#xff0c;是一个革命性的基于人工智能的艺术生成器&#xff0c;可以从被称为提示的简单文本描述中生成令人惊叹的图像。Midjourney已经迅速成为艺术家、设计师和营销人员的首选工具&#xff08;包括像我这样根本不会设计任何东西的无能之辈&#xff09;。 为了帮…

前端优化的分析

前端优化的分析 目录概述需求&#xff1a; 设计思路实现思路分析渲染层性能更好的API 参考资料和推荐阅读 Survive by day and develop by night. talk for import biz , show your perfect code,full busy&#xff0c;skip hardness,make a better result,wait for change,cha…

强化学习技巧

此软件包处于维护模式&#xff0c;请使用Stable-Baselines3 (SB3)获取最新版本。您可以在 SB3 文档中找到迁移指南。 本节的目的是帮助您进行强化学习实验。它涵盖了有关 RL 的一般建议&#xff08;从哪里开始、选择哪种算法、如何评估算法等&#xff09;&#xff0c;以及使用自…

嵌入式QT (Qt 信号与槽)

一、Qt 信号与槽机制 因为有了信号与槽的编程机制&#xff0c;在 Qt 中处理界面各个组件的交互操作时变得更加直观和简单。 信号&#xff08;Signal&#xff09;就是在特定情况下被发射的事件。 GUI 程序设计的主要内容就是对界面上各组件的信号的响应&#xff0c;只需要知道…

MySQL_第05章_排序与分页

第05章_排序与分页 讲师&#xff1a;尚硅谷 - 宋红康&#xff08;江湖人称&#xff1a;康师傅&#xff09; 官网&#xff1a; http://www.atguigu.com 1. 排序数据 1.1 排序规则 使用 ORDER BY 子句排序 ASC&#xff08;ascend&#xff09;: 升序 DESC&#xff08;desc…

物流管理APP软件开发公司 让货运变得更简单

随着互联网技术的发展&#xff0c;人们的生活方式也发生了很大的变化&#xff0c;移动互联网如今已经深入到生活的方方面面&#xff0c;就连物流运输行业也开始涌现出各种货运物流管理APP软件&#xff0c;让整个物流管理过程更加简单。下面我们一起来看一下为什么越来越多的运输…

数据库实验 | 第4关:修改多个数据表的存储过程

任务描述 本关任务&#xff1a; 图书管理数据库有读者reader图书book借阅数据表 读者表reader有读者证号dzzh、姓名xm、性别xb、身份sf、电话号码dhhm字段 图书表book有条形码txm、书名sm、分类号flh,作者zz,出版社cbs,出版日期cbrq,售价sj,典藏类别dclb,在库zk,币种bz字段 …

【DEBUG】错误手册集

文章目录 1.sshd启动报错,无法绑定端口2.克隆后的虚拟机可以联网&#xff0c;但是Xshell连接失败的解决办法 1.sshd启动报错,无法绑定端口 (1) 首先排查是否有端口占用&#xff08;没有发现问题&#xff09; netstat -ano | grep sshd(2) 查看 message 日志存在如下错误信息&…

MySQL数据库,联合查询

目录 1. 联合查询 1.1 内查询 1.2 外查询 1.3 自连接 1.4 子查询 1.5 合并查询 1. 联合查询 联合查询&#xff0c;简单的来讲就是多个表联合起来进行查询。这样的查询在我们实际的开发中会用到很多&#xff0c;因此会用笛卡尔积的概念。 啥是笛卡尔积&#xff1f;两张表…

深度学习 -- 张量操作与线性回归 张量的数学运算以及用张量构建线性回归模型

前言 这篇博客继承前篇博客的内容&#xff0c;将对张量的操作进行阐述&#xff0c;同时在理解张量的一些数学的基础上&#xff0c;配合机器学习的理论&#xff0c;在pytorch环境中进行一元线性回归模型的构建。 张量的拼接与切分 torch.cat() 功能&#xff1a;将张量按维度d…

Node.js四:包管理工具

1.介绍 2.npm安装 3.npm基本使用 属性翻译 4.搜索下载安装包 npm搜索网站&#xff1a; npm 5.生产环境与开发环境 开发环境 是程序员 专门用来写代码 的环境&#xff0c;一般是指程序员的电脑&#xff0c;开发环境的项目一般 只能程序员自己访问 生产环境 是项目 代码正式运行 …

海光信息业绩高歌猛进,但其作为国产CPU龙头的“地基”并不牢固

‍数据智能产业创新服务媒体 ——聚焦数智 改变商业 在“芯片寒冬”的大背景下&#xff0c;2022年全球头部芯片半导体公司纷纷下调业绩预期&#xff0c;英特尔、英伟达、美光等无一幸免。但是随着AIGC异军突起&#xff0c;仿佛寒冬中的一股暖流&#xff0c;催生着半导体市场行…

第四章 面向对象(OOP)

目录 一、编程思想 1.1. 面向对象 1.2. 面向过程 1.3.举例说明&#xff08;把大象装进冰箱&#xff09; 1.4.二者的联系与区别 1.5.面向对象的三个阶段 1.6.什么是类&#xff0c;什么是实例&#xff0c;二者的联系 二、面向对象三大特征 2.1 封装 2.2 继承 2.3 多态…

go-zero入门

文章目录 简介框架设计环境准备go安装Go Module设置goctl安装安装(mac\&linux)安装(windows) protoc & protoc-gen-go安装 goctl 各层代码生成一览goctl 生成REST和RPC微服务生成数据模型层Model生成API服务目录结构开发者需要做的修改配置文件修改上下文依赖修改业务逻…

CMake基本使用

重要指令 cmake_minimum_required:指定CMake最小版本要求 project&#xff1a;定义工程名称&#xff0c;并可指定语言 set&#xff1a;显示的定义变量 include_directories&#xff1a;向工程添加多个特定头文件搜素路径 link_directories&#xff1a;向工程添加多个特定库文件…

Spring lettuce读写分离

Redis 的 Sentinel 模式默认配置下 Redis 的客户端只对 Master 读写&#xff0c;另外2个Slave闲置。若主从节点在不同机房&#xff0c;在读取时会有跨机房的网络时延&#xff0c;并且比同机房访问更容易发生网络丢包。故在一些场景可以考虑将跨机房的服务节点设置为读写分离 Re…

计算机组成原理——第五章中央处理器(下)

梦里不知身是客&#xff0c;一晌贪欢 文章目录 5.6.1 指令流水线的基本概念5.6.2 指令流水线的影响因素和分类五段式指令流水线5.7.1 多处理器的基本概念5.7.2 硬件多线程的基本概念 5.6.1 指令流水线的基本概念 想要对指令的过程进行优化&#xff0c;一条指令的执行过程可以被…

世界新冠疫情数countrydata.csv 表,实战分析

一、环境要求 Hadoop hive spark hbase开发环境 开启hadoop&#xff1a;start-all.sh开启zookeeper&#xff1a;zkServer.sh start开启hive&#xff1a;nohup hive --service metastore &nohup hive --service hiveserver2 & 打开hive界面&#xff1a;beeline -u …

单列集合之Set集合以及各种实现类

Set集合 Set接口也是Collection单列结合的一个子接口&#xff0c;set集合中没有提供其他额外的方法&#xff0c;但是相比较Collection集合新增了其他的特性。所有实现了Set接口的类都可以叫做Set集合。 Coliection接口中的方法&#xff1a;Collection集合的方法 Set集合不允…