结构型设计模式之组合模式【设计模式系列】

news2025/1/9 17:17:42

系列文章目录

C++技能系列
Linux通信架构系列
C++高性能优化编程系列
深入理解软件架构设计系列
高级C++并发线程编程
设计模式系列

期待你的关注哦!!!
在这里插入图片描述

现在的一切都是为将来的梦想编织翅膀,让梦想在现实中展翅高飞。
Now everything is for the future of dream weaving wings, let the dream fly in reality.

结构型设计模式之组合模式

  • 系列文章目录
  • 一、组合模式介绍
  • 二、组合模式优缺点
    • 2.1 优点
    • 2.2 缺点
  • 三、组合模式使用场景
  • 四、组合模式实现

一、组合模式介绍

⚠️ 意图:
将对象组合成树形结构以表示"部分-整体"的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。

⚠️ 主要解决:
它在我们树型结构的问题中,模糊了简单元素和复杂元素的概念,客户程序可以像处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦。

⚠️ 何时使用:
1、您想表示对象的部分-整体层次结构(树形结构)。 2、您希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。

⚠️ 如何解决:
树枝和叶子实现统一接口,树枝内部组合该接口。

现有一个公司需要实现一个广播的功能,用来通知给公司所有部门重要的信息。如果需要广播,那么必须通过每一个部门类的实例去调用各自部门的广播接口。通过一个抽象类来抽象出广播接口,各个部门和公司都通过继承实现抽象接口。如此实现可以将所有部门放在一个容器内,遍历实现其广播接口。如果公司不扩大,永远有这几个部门并且没有分公司的话,是没问题的。如果再要添加一个部门,就必须实现一个部门类,再修改总公司类。这明显违背了开闭原则。既然我们通过抽象类来实现了其方法,那就可以实现抽象的增加,删除,查询接口。如此一来,如果要有新的部门, 那么只需要通过调用添加接口来增加新部门, 广播的时候,只需要遍历容器中的广播接口即可。实现类图如下:

在这里插入图片描述

图1_1 组合模式类图

组合模式,将对象组合成树形结构以表示“部分-整体”的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。掌握组合模式的重点是要理解清楚 “部分/整体” 还有 ”单个对象“ 与 “组合对象” 的含义。

组合模式可以让客户端像修改配置文件一样简单的完成本来需要流程控制语句来完成的功能。

二、组合模式优缺点

2.1 优点

  • 简化客户端调用,实现符合开闭原则。
  • 高层模块调用简单。
  • 节点自由增加

2.2 缺点

  • 如果业务逻辑负责,则实现组合模式比较困难。

  • 在使用组合模式时,其叶子和树枝的声明都是实现类,而不是接口,违反了依赖倒置原则。

三、组合模式使用场景

适用于“整体-部分”层次的树形结构的。部分、整体场景,如树形菜单,文件、文件夹的管理。

客户端对组合对象统一的使用所有对象。

四、组合模式实现

Component.h

#ifndef COMPONENTS_H_
#define COMPONENTS_H_

#include <iostream>
#include <vector>

using namespace std;

class Components
{
public:
    Components(std::string strName):m_strName(strName){}

    virtual void Operation() = 0;
    virtual void AddSubCompany(Components* subCompany);
    virtual void DelSubCompany(Components* subCompany);
    virtual Components* GetCompanyByIndex(int iIndex);

protected:
    std::string m_strName;
};

class ConcreteCompany : public Components
{
public:
    ConcreteCompany(std::string strName):Components(strName){}
    ~ConcreteCompany();

    virtual void Operation();
    virtual void AddSubCompany(Components* subCompany);
    virtual void DelSubCompany(Components* subCompany);
    virtual Components* GetCompanyByIndex(int iIndex);
private:
    std::vector<Components*> m_vecSubItem;
};

class FinanceDepartment : public Components
{
public:
    FinanceDepartment(std::string strName):Components(strName){}

    virtual void Operation();
};

class HRDepartment : public Components
{
public:
    HRDepartment(std::string strName):Components(strName){}

    virtual void Operation();
};

#endif

Component.cpp

#include "Company.h"
#include <algorithm>

#ifndef SAFE_DELETE
#define SAFE_DELETE(p){if((p) != NULL){delete (p); (p) = NULL;}}
#endif

void Components::AddSubCompany( Components* subCompany )
{
    cout << "Have no realized!" << endl;
}

void Components::DelSubCompany( Components* subCompany )
{
    cout << "Have no realized!" << endl;
}

Components* Components::GetCompanyByIndex( int iIndex )
{
    cout << "Have no realized!" << endl;
    return NULL;
}

//******************//
//**CentralCompany**//
//******************//

ConcreteCompany::~ConcreteCompany()
{
    std::for_each(m_vecSubItem.begin(),m_vecSubItem.end(),[&](Components* item)
    {
        SAFE_DELETE(item);
    });
}

void ConcreteCompany::Operation()
{
    std::for_each(m_vecSubItem.begin(),m_vecSubItem.end(),[&](Components* item)
    {
        item->Operation();
    });
}

void ConcreteCompany::AddSubCompany( Components* subCompany )
{
    if (subCompany != NULL)
    {
        m_vecSubItem.push_back(subCompany);
    }
}

void ConcreteCompany::DelSubCompany( Components* subCompany )
{
    for (auto it = m_vecSubItem.begin(); it != m_vecSubItem.end(); ++it)
    {
        if ((*it) == subCompany)
        {
            m_vecSubItem.erase(it);
            SAFE_DELETE(subCompany);
            break;
        }
    }
}

Components* ConcreteCompany::GetCompanyByIndex( int iIndex )
{
    if (iIndex < 0 || iIndex > m_vecSubItem.size())
    {
        return NULL;
    }
    return m_vecSubItem[iIndex];
}

void FinanceDepartment::Operation()
{
    cout << m_strName.c_str() << endl;
}

void HRDepartment::Operation()
{
    cout << m_strName.c_str() << endl;
}

main.cpp

#include <iostream>
#include "Company.h"

using namespace std;

int main()
{
    Components* Central = new ConcreteCompany("Central Company");
    Central->AddSubCompany(new FinanceDepartment("Central Finance Department"));
    Central->AddSubCompany(new HRDepartment("Central HR Department"));

    Components* Xian = new ConcreteCompany("Xi'An Company");
    Xian->AddSubCompany(new FinanceDepartment("Xi'An Finance Department"));
    Xian->AddSubCompany(new HRDepartment("Xi'An HR Department"));

    Central->AddSubCompany(Xian);
    Central->Operation();

    cout << "<<<<<>>>>>" << endl;
    Central->DelSubCompany(Xian);
    Central->Operation();

    return 0;
}

输出:

Central Finance Department

Central HR Department

Xi’An Finance Department

Xi’An HR Department

<<<<<>>>>>

Central Finance Department

Central HR Department

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

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

相关文章

【nginx】nginx中root与alias的区别:

文章目录 root与alias主要区别在于nginx如何解释location后面的uri&#xff0c;这会使两者分别以不同的方式将请求映射到服务器文件上。 root的处理结果是&#xff1a;root路径&#xff0b;location路径 alias的处理结果是&#xff1a;使用alias路径替换location路径 alias是一…

前端配置nginx反向代理

1.下载nginx 2.进入nginx.conf 3.配置 4.将nginx放入前端项目根目录 5.启动后端项目和nginx服务 启动命令 start nginx ; 重新启动命令 nginx -s reload 6.访问localhost就能看到项目了&#xff08;默认80端口&#xff09; 注意&#xff1a;如果nginx配置了域名&#xff0…

Android ConstraintLayout使用攻略

原文链接 Android ConstraintLayout使用攻略 ConstraintLayout是新一代的布局&#xff0c;它汲取了众家之长&#xff0c;把布局的概念进行了大统一&#xff0c;灵活且强大&#xff0c;基本上可以干掉以前所有的常用布局&#xff08;LinearLayout, RelativeLayout和FrameLayout…

94.qt qml-分页Table表格组件

在我们之前学习了87.qt qml-分页组件控件(支持设置任意折叠页数等)_qt分页控件_诺谦的博客-CSDN博客 然后我们又学习了Table实现,所以本章实现一个分页Table表格组件,配合分页控件, 模拟请求服务器数据来实现数据分解效果,因为一般使用分页的时候,一般都是分页请求,避免数…

Flutter的开发环境搭建-图解

前言&#xff1a;Flutter作为一个移动应用开发框架&#xff0c;具有许多优点和一些局限性。最大的优点就是-跨平台开发&#xff1a;Flutter可以在iOS和Android等多个平台上进行跨平台开发&#xff0c;使用一套代码编写应用程序&#xff0c;节省开发时间和成本。 Flutter可以编…

python将大文件拆分为多个小文件

如上图&#xff0c;目前采用单行不停写入的方式&#xff0c;这里是读了两次文件&#xff0c;第一次读取文件是为了获取总行数&#xff0c;第二次读取是取数据内容。 如果只读取一次文件&#xff0c;则会对内存有一定的要求&#xff0c;会需要在第一次读取数据的时候就将文件内…

ONNX Runtime 加速深度学习(C++ 、python)详细介绍

ONNX Runtime 加速深度学习(C 、python)详细介绍 本文在 https://blog.csdn.net/u013250861/article/details/127829944 基础上进行了更改&#xff0c;感谢原作&#xff01; ONNXRuntime(Open Neural Network Exchange)是微软推出的一款针对ONNX模型格式的推理框架&#xff0c…

Redis常用数据类型和使用场景

Redis目前支持5种数据类型&#xff0c;分别是&#xff1a; String&#xff08;字符串&#xff09; List&#xff08;列表&#xff09; Hash&#xff08;字典&#xff09; Set&#xff08;集合&#xff09; Sorted Set&#xff08;有序集合&#xff09; 下面就分别介绍这五…

Qt Core学习日记——第六天QMetaMethod

Qt子类会将每一个函数封装成QMetaMethod存储在对应的QMetaObject中&#xff0c;包括信号、槽函数、普通函数、构造函数、析构函数 函数解析 QMetaMethod::methodSignature 获取方法的签名 比如函数slot2&#xff0c;对应签名是“slot2(int*)” QMetaMethod::name 方法名称。…

13.2.3 【Linux】新增与移除群组

基本上&#xff0c;群组的内容都与这两个文件有关&#xff1a;/etc/group, /etc/gshadow。 群组的内容其实很简单&#xff0c;都是上面两个文件的新增、修改与移除而已。 groupadd 为了让使用者的 UID/GID 成对&#xff0c;建议新建的与使用者私有群组无关的其他群组时&#x…

RabbitMQ入门,springboot整合RabbitMQ

周末的两天没有写文章&#xff0c;因为项目分离出来了一个权限管理平台&#xff0c;花了一点时间整理项目&#xff0c;同时完成了一些功能的开发。 今天这篇文章介绍一下RabbitMQ这个消息中间件&#xff0c;以及通过springboot整合RabbitMQ。 目录 一、初步了解RabbitMQ 二、…

学Java有哪些就业方向?

俗话说&#xff1a;男怕入错行&#xff0c;女怕嫁错郎。众所周知&#xff0c;选工作就是选行业&#xff0c;行业和方向选对了&#xff0c;个人的发展就会随着行业风向青云直上&#xff0c;比同龄人更快的积累到财富。那究竟未来什么会是热门行业呢?这个真的很难预测&#xff0…

【1++的C++初阶】之模板(二)

&#x1f44d;作者主页&#xff1a;进击的1 &#x1f929; 专栏链接&#xff1a;【1的C初阶】 文章目录 一&#xff0c;非类型模板参数二&#xff0c;模板特化三&#xff0c;模板分离编译 一&#xff0c;非类型模板参数 模板参数分为类类型模板参数与非类型模板参数。 类类型形…

【雕爷学编程】Arduino动手做(167)---MG996R金属齿轮舵机2

37款传感器与模块的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&#x…

苹果“空间音频导航”专利曝光,提供导航指引,跟声音走就对啦?

近日&#xff0c;苹果公司成功申请一项专利&#xff0c;该专利名为“空间音频导航”&#xff0c;该专利详细说明了如何利用双耳音频设备&#xff08;AirPods或Apple Vision Pro&#xff09;为用户提供导航指引。 “空间音频导航”是一种模拟声音来源方向和距离的技术&#xff0…

STM32MP157驱动开发——按键驱动(POLL 机制)

文章目录 “POLL ”机制&#xff1a;APP执行过程驱动使用的函数应用使用的函数pollfd结构体poll函数事件类型实现原理 poll方式的按键驱动程序(stm32mp157)gpio_key_drv.cbutton_test.cMakefile修改设备树文件编译测试 “POLL ”机制&#xff1a; 使用休眠-唤醒的方式等待某个…

c# Outlook检索设定问题

基于c# 设定outlook约会予定&#xff0c;时间格式是YYYY-MM-DD HH:mm 的情报。 问题发生&#xff1a; 根据开始时间&#xff08;2023/01/01 7:00&#xff09;条件查询该时间是否存在outlook信息时&#xff0c;明明存在一条数据&#xff0c;就是查询不出来数据 c#代码 Strin…

单源最短路的扩展应用

AcWing 1137. 选择最佳线路 有一天&#xff0c;琪琪想乘坐公交车去拜访她的一位朋友。 由于琪琪非常容易晕车&#xff0c;所以她想尽快到达朋友家。 现在给定你一张城市交通路线图&#xff0c;上面包含城市的公交站台以及公交线路的具体分布。 已知城市中共包含 n 个车站…

解决 Visual Studio Code 编译器代码自动格式化

首先找到.vscode下的settings.json配置文件 将vue3snippets.enable-compile-vue-file-on-did-save-code更改为false

多个HttpSecurity配置(局部AuthenticationManager)

前言 项目用的ruoyi的扩展版本(ts版本)&#xff0c;如果有缺失类&#xff0c;可以自行下载或补充------》个人理解 实现多端token&#xff0c;多端httpSecurity&#xff0c;并且相互隔离&#xff08;局部AuthenticationManager管理认证及授权&#xff09; 在最近的项目中遇到一…