突破编程_C++_设计模式(组合模式)

1 组合模式的基本概念

C++中的组合模式是一种对象结构型模式,它将多个对象组合成树形结构,以表示具有整体-部分关系的层次结构。在这个模式中,对单个对象(叶子对象)与组合对象(容器对象)的使用具有一致性,因此组合模式又称为部分-整体模式。

组合模式的主要概念包括:

(1)抽象组件(Component): 这是组合模式中最重要的接口或抽象类,它声明了访问及管理子组件的方法,如增加子组件、删除子组件、获取子组件等。所有叶子组件和容器组件都应当实现这个接口。

(2)叶子组件(Leaf): 这是组合模式中的基本对象,它实现了抽象组件中定义的行为,并且没有子组件。

(3)容器组件(Composite): 这是包含子组件的组合对象,它可以包含叶子组件和其他容器组件,形成一个树形结构。容器组件实现了抽象组件中定义的行为,并提供了管理和操作其子组件的方法。

使用组合模式的主要优点在于,客户端可以一致地处理单个对象和组合对象,无需关心它们之间的具体差异。这使得代码更加简洁、清晰,并提高了系统的灵活性和可维护性。

组合模式在多种场景中都非常有用,例如当需要表示具有整体与部分关系的层次结构时,或者当希望忽略整体与部分的差异,以一种统一的方式处理它们时。此外,当系统中需要处理树形结构,或者需要动态地增加新的类型时,组合模式也是一个很好的选择。

2 组合模式的实现步骤

组合模式的实现步骤如下:

(1)定义抽象组件(Component)接口:
创建一个抽象类或接口,表示组件的通用行为。
在该接口中,定义用于操作组件的方法,比如添加、删除子组件,获取子组件等。

(2)实现叶子组件(Leaf)类:
创建一个或多个叶子组件类,继承或实现抽象组件接口。
在叶子组件类中,实现抽象组件接口中定义的方法,但通常叶子组件不会有子组件,所以添加和删除子组件的方法可能会抛出异常或执行空操作。

(3)实现容器组件(Composite)类:
创建一个容器组件类,继承或实现抽象组件接口。
在容器组件类中,除了实现抽象组件接口中定义的方法外,还需要维护一个子组件列表。
提供方法来添加、删除和获取子组件,并可以递归地调用子组件的相应方法。

(4)组合组件:
在客户端代码中,可以创建叶子组件和容器组件的实例,并将它们组合起来形成树形结构。
客户端可以通过容器组件的接口来操作整个树形结构,无论是访问叶子组件还是递归地操作子树。

如下为样例代码:

#include <iostream>  
#include <vector>  
#include <string>  
#include <memory>  
#include <algorithm>  // 定义抽象组件(Component)接口  
class Component {
public:virtual ~Component() {}virtual void operation() const = 0;virtual void add(std::shared_ptr<Component> component) = 0;virtual void remove(Component* component) = 0;virtual Component* getChild(int index) = 0;virtual int getNumChildren() const = 0;virtual bool isComposite() const = 0;virtual bool isLeaf() const = 0;
};// 实现叶子组件(Leaf)类  
class Leaf : public Component {
public:Leaf(const std::string& name) : m_name(name) {}void operation() const override {std::cout << "Leaf operation: " << m_name << std::endl;}void add(std::shared_ptr<Component> component) override {std::cout << "Leaf cannot have children." << std::endl;}void remove(Component* component) override {std::cout << "Leaf cannot remove children." << std::endl;}Component* getChild(int index) override {return nullptr;}int getNumChildren() const override {return 0;}bool isComposite() const override {return false;}bool isLeaf() const override {return true;}private:std::string m_name;
};// 实现容器组件(Composite)类  
class Composite : public Component {
public:Composite(const std::string& name) : m_name(name) {}void operation() const override {std::cout << "Composite operation: " << m_name << std::endl;for (const auto& child : m_children) {child->operation();}}void add(std::shared_ptr<Component> component) override {m_children.push_back(std::move(component));}void remove(Component* component) override {auto it = std::find_if(m_children.begin(), m_children.end(),[component](const std::shared_ptr<Component>& c) {return c.get() == component;});if (it != m_children.end()) {m_children.erase(it);}else {std::cout << "Component not found in children." << std::endl;}}Component* getChild(int index) override {if (index < 0 || index >= m_children.size()) {return nullptr;}return m_children[index].get();}int getNumChildren() const override {return m_children.size();}bool isComposite() const override {return true;}bool isLeaf() const override {return false;}private:std::string m_name;std::vector<std::shared_ptr<Component>> m_children;
};// 组合组件  
int main() 
{// 创建叶子组件和容器组件实例  std::shared_ptr<Leaf> leaf1 = std::make_shared<Leaf>("Leaf1");std::shared_ptr<Leaf> leaf2 = std::make_shared<Leaf>("Leaf2");std::shared_ptr<Composite> composite = std::make_shared<Composite>("Composite");// 将叶子组件添加到容器组件中  composite->add(std::move(leaf1));composite->add(std::move(leaf2));// 执行操作  composite->operation();// 移除组件(如果需要)  // composite->remove(leaf1.get());  return 0;
}

上面代码的输出为:

Composite operation: Composite
Leaf operation: Leaf1
Leaf operation: Leaf2

在这个示例中,定义了一个 Component 抽象类,它包含了组件的通用行为。Leaf 类表示叶子组件,它实现了 Component 接口,但通常不会有子组件。Composite 类表示容器组件,它同样实现了 Component 接口,并维护了一个子组件列表。

这个示例展示了组合模式的基本结构和使用方式。可以根据需要扩展这个示例,添加更多的组件类型、方法或操作,以适应具体应用场景。

3 组合模式的应用场景

C++ 中的组合模式应用场景主要涉及到需要表示对象的整体-部分层次结构,并希望客户端能够以统一的方式处理单个对象和对象组合的情况。以下是一些具体的应用场景:

(1)文件系统: 文件系统中的文件和文件夹是典型的组合模式应用场景。文件夹可以包含文件和其他文件夹,形成树形结构。通过组合模式,用户可以统一地处理文件和文件夹,如进行遍历、搜索、删除等操作,而无需区分它们的具体类型。

(2)图形用户界面(GUI): 在GUI中,窗口、面板、按钮、文本框等组件经常需要组合使用。组合模式允许将这些组件组织成树形结构,使得用户可以通过统一的接口来管理和操作这些组件。例如,一个窗口可以包含多个面板,而面板又可以包含按钮和文本框等子组件。

(3)菜单系统: 在复杂的菜单系统中,菜单项可能包含子菜单,形成多级菜单结构。通过组合模式,可以方便地管理这种多级菜单,并允许用户通过统一的接口进行导航和操作。

(4)部门组织架构: 在企业或组织的部门结构中,部门可能包含子部门或员工,形成层次化的组织结构。组合模式可以用于表示这种组织结构,并允许管理层以统一的方式管理和查询各个部门或员工的信息。

在这些应用场景中,组合模式使得客户端代码更加简洁和清晰,因为客户端只需要与抽象组件接口进行交互,而无需关心具体是处理单个对象还是对象组合。此外,组合模式还具有良好的扩展性,可以方便地添加新的组件类型或修改现有组件的行为,而无需修改客户端代码。

3.1 组合模式应用于文件系统

C++ 组合模式应用于文件系统的示例可以描述如下:

首先,定义一个 FileSystemElement 抽象类,它表示文件系统中的元素,可以是文件或文件夹。这个类将声明一些基本的操作,如名称的获取和设置。

#include <iostream>  
#include <string>  
#include <vector>  
#include <memory>  class FileSystemElement {
public:virtual ~FileSystemElement() {}virtual std::string getName() const = 0;virtual void setName(const std::string& name) = 0;// 可能还有其他通用操作,如访问权限设置等  
};

然后,实现 File 类,表示文件系统中的一个文件。它继承自 FileSystemElement,并实现其中的方法。

class File : public FileSystemElement {
public:File(const std::string& name) : name(name) {}std::string getName() const override {return name;}void setName(const std::string& name) override {this->name = name;}// 文件特有的操作,如读取内容、写入内容等 private:std::string name;
};

接下来,实现 Directory 类,表示文件系统中的一个文件夹。它也继承自 FileSystemElement,并实现其中的方法。此外,它还需要维护一个包含子元素的列表。

class Directory : public FileSystemElement {
public:Directory(const std::string& name) : name(name) {}std::string getName() const override {return name;}void setName(const std::string& name) override {this->name = name;}void addChild(std::shared_ptr<FileSystemElement> child) {children.push_back(child);}// 文件夹特有的操作,如遍历子元素、删除子元素等  void traverse() const {for (const auto& child : children) {std::cout << child->getName() << std::endl;if (auto dir = dynamic_cast<const Directory*>(child.get())) {dir->traverse(); // 递归遍历子文件夹  }}}private:std::string name;std::vector<std::shared_ptr<FileSystemElement>> children;
};

现在,可以使用这些类来构建文件系统树:

int main() 
{// 创建文件夹和文件对象  std::shared_ptr<Directory> root = std::make_shared<Directory>("root");std::shared_ptr<Directory> dir1 = std::make_shared<Directory>("dir1");std::shared_ptr<File> file1 = std::make_shared<File>("file1.txt");std::shared_ptr<File> file2 = std::make_shared<File>("file2.txt");// 将文件和文件夹添加到树中  root->addChild(dir1);dir1->addChild(file1);dir1->addChild(file2);// 遍历并打印文件系统树的内容  root->traverse();return 0;
}

上面这些代码的输出为:

dir1
file1.txt
file2.txt

这个简单的示例展示了如何使用组合模式来构建一个文件系统的树形结构,并允许通过统一的方式处理文件和文件夹。在实际应用中,文件系统通常会有更多的操作和功能,如文件的读写、权限管理、路径解析等,但这些都可以基于这个基本框架进行扩展。

3.2 组合模式应用于图形用户界面(GUI)

C++ 组合模式应用于图形用户界面(GUI)的示例可以描述如下:

首先,定义一个 GUIComponent 抽象类,它表示 GUI 中的一个组件。这个类将声明一些基本的操作,如绘制、添加子组件等。

#include <iostream>  
#include <vector>  
#include <memory>   
#include <string>   
#include <algorithm> class GUIComponent {
public:virtual ~GUIComponent() {}virtual void draw() const = 0;virtual void add(std::shared_ptr<GUIComponent> component) = 0;virtual void remove(GUIComponent* component) = 0;// 可能还有其他通用操作,如设置位置、大小等  
};

然后,实现 LeafComponent 类,表示 GUI 中的一个叶子组件,比如一个按钮或文本框。它继承自 GUIComponent,并实现其中的方法。

class LeafComponent : public GUIComponent {
public:LeafComponent(const std::string& name) : name(name) {}void draw() const override {std::cout << "Drawing leaf component: " << name << std::endl;}void add(std::shared_ptr<GUIComponent> component) override {// 叶子组件不能添加子组件,可以抛出异常或忽略  std::cout << "Leaf component cannot add child components." << std::endl;}void remove(GUIComponent* component) override {// 叶子组件没有子组件可移除,可以抛出异常或忽略  std::cout << "Leaf component has no child components to remove." << std::endl;}private:std::string name;
};

接下来,实现 CompositeComponent 类,表示 GUI 中的一个容器组件,比如一个窗口或面板。它也继承自 GUIComponent,并实现其中的方法。此外,它还需要维护一个包含子组件的列表。

class CompositeComponent : public GUIComponent {
public:CompositeComponent(const std::string& name) : name(name) {}void draw() const override {std::cout << "Drawing composite component: " << name << std::endl;for (const auto& child : children) {child->draw(); // 递归绘制子组件  }}void add(std::shared_ptr<GUIComponent> component) override {children.push_back(std::move(component));}void remove(GUIComponent* component) override {auto it = std::find_if(children.begin(), children.end(),[component](const std::shared_ptr<GUIComponent>& c) {return c.get() == component;});if (it != children.end()) {children.erase(it);}else {std::cout << "Component not found in children." << std::endl;}}private:std::string name;std::vector<std::shared_ptr<GUIComponent>> children;
};

现在,可以使用这些类来构建GUI:

int main() 
{// 创建叶子组件和容器组件对象  std::shared_ptr<LeafComponent> button = std::make_shared<LeafComponent>("Button");std::shared_ptr<LeafComponent> textBox = std::make_shared<LeafComponent>("TextBox");std::shared_ptr<CompositeComponent> window = std::make_shared<CompositeComponent>("Window");// 将叶子组件添加到容器组件中  window->add(button);window->add(textBox);// 绘制GUI  window->draw();// 移除组件(如果需要)  // window->remove(button.get()); // 假设想要移除按钮  return 0;
}

上面这些代码的输出为:

Drawing composite component: Window
Drawing leaf component: Button
Drawing leaf component: TextBox

这个简单的示例展示了如何使用组合模式来构建一个GUI,并允许通过统一的方式处理容器组件和叶子组件。在实际应用中,GUI组件通常会有更多的属性和方法,比如处理用户事件、更新状态等,但这些都可以基于这个基本框架进行扩展。

4 组合模式的优点与缺点

C++ 组合模式的优点主要包括:

(1)结构清晰: 组合模式可以清晰地定义分层次的复杂对象,表示对象的全部或部分层次,使得客户端能够忽略对象组合和单个对象的区别,一致地对待组合结构中的所有对象。这有助于简化客户端代码,提高代码的可读性和可维护性。

(2)扩展性好: 在组合模式中,增加新的容器构件和叶子构件都很方便,无需对现有类库进行任何修改,这符合“开闭原则”。这使得系统的扩展和修改变得更加容易,降低了系统的耦合度。

(3)灵活性高: 组合模式可以灵活地将不同的叶子节点抽象成相同的节点,达成忽略整体-部分差异的目标,最终给用户的是统一的抽象接口。这使得系统能够更灵活地应对各种复杂情况,提高了系统的适应性和灵活性。

然而,C++ 组合模式也存在一些缺点:

(1)设计抽象性: 组合模式可能使设计变得过于抽象和复杂。如果对象的业务规则很复杂,实现组合模式可能会具有很大的挑战性。此外,并非所有的方法都与叶子对象子类都相关联,这可能导致一些不必要的复杂性。

(2)通用性问题: 在某些情况下,只有叶子组件需要定义某些操作,但由于组合模式的通用性,可能不得不在所有组件中定义这些操作,这可能会增加代码的复杂性。

(3)对象创建与转换问题: 当组合深度很大时,创建对象可能会变得非常麻烦,并且会创建大量的子对象。此外,在进行类型转换时,需要避免截断使用范围,否则可能会导致错误。

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

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

相关文章

HPE ProLiant MicroServer Gen8 RAID 1 扩容、故障恢复探索

声明&#xff1a;本探索只针对HP Microserver Gen8 Smart Array B120i板载阵列卡组建RAID 1&#xff08;不是RAID 10&#xff09;&#xff0c;不同阵列卡结果可能会不同&#xff0c;请谨慎操作&#xff0c;本人不对你操作后导致的任何数据丢失或者损失承担任何责任&#xff0c;…

kafka Interceptors and Listeners

Interceptors ProducerInterceptor https://www.cnblogs.com/huxi2b/p/7072447.html Producer拦截器(interceptor)是个相当新的功能&#xff0c;它和consumer端interceptor是在Kafka 0.10版本被引入的&#xff0c;主要用于实现clients端的定制化控制逻辑。 对于producer而言&…

Java SE入门及基础(29)

第三节 访问修饰符 1. 概念 访问修饰符就是控制访问权限的修饰符号 2. 类的访问修饰符 类的访问修饰符只有两种&#xff1a;public 修饰符和默认修饰符&#xff08;不写修饰符就是默认&#xff09; public 修饰符修饰类表示类可以公开访问。默认修饰符修饰类表示该类只能…

融资项目——网关微服务

1. 网关的路由转发功能 在前后端分离的项目中&#xff0c;网关服务可以将前端的相关请求转发到相应的后端微服务中。 2. 网关微服务的配置 首先需要创建一个网关微服务&#xff0c;并添加依赖。 <!-- 网关 --><dependency><groupId>org.springframework.cl…

wpf中的Border和Background

在WPF&#xff08;Windows Presentation Foundation&#xff09;中&#xff0c;Border和Background是两个非常重要的属性&#xff0c;它们通常用于定义用户界面元素的外观样式。 Border&#xff1a; Border 是一个控件&#xff0c;它本身是一个装饰器&#xff0c;允许您为任何…

FreeRTOS学习笔记-基于stm32(3)中断管理

一、什么是中断 通俗点讲就是让CPU停止当前在做的事&#xff0c;转而去做更紧急的事。 二、中断优先级分组 这个紧急的事也有一个等级之分&#xff0c;优先级越高越先执行。stm32使用中断优先配置寄存器的高4位&#xff0c;共16级的中断优先等级。 stm32的中断优先等级可以分为…

让开源浏览器Chromium正常显示中文

什么是 Chromium &#xff1f; Chromium 是一个开源浏览器项目&#xff0c;旨在为所有用户构建一种更安全、更快、更稳定的网络体验方式。 和老苏之前介绍的 Firefox 的作用是一样的 文章传送门&#xff1a;给群晖安装firefox浏览器 因为是基于 vnc 的应用&#xff0c;感觉资源…

Elasticsearch 通过索引阻塞实现数据保护深入解析

Elasticsearch 是一种强大的搜索和分析引擎&#xff0c;被广泛用于各种应用中&#xff0c;以其强大的全文搜索能力而著称。 不过&#xff0c;在日常管理 Elasticsearch 时&#xff0c;我们经常需要对索引进行保护&#xff0c;以防止数据被意外修改或删除&#xff0c;特别是在进…

mysql笔记:1. 数据库创建与删除

可以使用show databases语句来查看当前所有存在的数据库。 mysql> show databases; Database ---------- information_schema mysql performance_schema sys其中&#xff0c;mysql用来描述用户访问权限。 创建数据库 创建数据库可以使用create database命令。 例如&#…

动态规划 第一期 背包问题

前言 动态规划难度较高&#xff0c;但是也十分重要&#xff0c;希望大家能够好好的理解&#xff01;&#xff01;&#xff01; 一、背包问题 思维导图&#xff1a; 背包问题(Knapsack problem)是一种组合优化的NP完全问题。问题可以描述为&#xff1a;给定一组物品&#xff…

c++ primer中文版第五版作业第十四章

见仓库 文章目录 14.114.214.314.414.514.614.714.814.914.1014.1114.1214.1314.1414.1514.1614.1714.1814.1914.2014.2114.2214.2314.2414.2514.2614.2714.2814.2914.3014.3114.3214.3314.3414.3514.3614.3714.3814.3914-4014.4114.4214.4314.4414.4514.4614.4714.4814.4914.5…

实现QT中qDebug()的日志重定向

背景&#xff1a; 在项目开发过程中&#xff0c;为了方便分析和排查问题&#xff0c;我们需要将原本输出到控制台的调试信息写入日志文件&#xff0c;进行持久化存储&#xff0c;还可以实现日志分级等。 日志输出格式&#xff1a; 我们需要的格式包括以下内容&#xff1a; 1.…

MySQL死锁详细介绍

首先死锁产生的原因&#xff1a;两个及以上事务争夺资源导致互相等待造成的 比如事务A先修改id为1的数据再去修改id为2的数据&#xff0c;事务B先修改id为2的数据再去修改id为1的数据&#xff0c;因为事务A先拿到id1的锁再去拿id2的锁&#xff0c;而事务B先拿到id2的锁又去拿id…

“成像光谱遥感技术中的AI革命:ChatGPT应用指

遥感技术主要通过卫星和飞机从远处观察和测量我们的环境&#xff0c;是理解和监测地球物理、化学和生物系统的基石。ChatGPT是由OpenAI开发的最先进的语言模型&#xff0c;在理解和生成人类语言方面表现出了非凡的能力。本文重点介绍ChatGPT在遥感中的应用&#xff0c;人工智能…

【Algorithms 4】算法(第4版)学习笔记 16 - 4.2 有向图

文章目录 前言参考目录学习笔记1&#xff1a;介绍1.1&#xff1a;有向图简介1.2&#xff1a;应用举例1.3&#xff1a;相关问题2&#xff1a;有向图 API2.1&#xff1a;有向图表示2.1.1&#xff1a;邻接表数组 Adjacency-list2.1.2&#xff1a;Java 实现&#xff1a;邻接表数组2…

2. gin中间件注意事项、路由拆分与注册技巧

文章目录 一、中间件二、Gin路由简介1、普通路由2、路由组 三、路由拆分与注册1、基本的路由注册2、路由拆分成单独文件或包3、路由拆分成多个文件4、路由拆分到不同的APP 一、中间件 在日常工作中&#xff0c;经常会有一些计算接口耗时和限流的操作&#xff0c;如果每写一个接…

Sftp服务器搭建(linux)

Sftp服务器搭建&#xff08;linux&#xff09; 一、基本工作原理 FTP的基本工作原理如下&#xff1a; 1&#xff09;建立连接&#xff1a;客户端与服务器之间通过TCP/IP建立连接。默认情况下&#xff0c;FTP使用端口号21作为控制连接的端口。​​​​​​​ 2&#xff09;身…

基于AI软件平台 HEGERLS智能托盘四向车机器人物流仓储解决方案持续升级

随着各大中小型企业对仓储需求的日趋复杂&#xff0c;柔性、离散的物流子系统也不断涌现&#xff0c;各种多类型的智能移动机器人、自动化仓储装备大量陆续的应用于物流行业中&#xff0c;但仅仅依靠传统的物流技术和单点的智能化设备&#xff0c;已经无法更有效的应对这些挑战…

Office 2007软件安装教程(附软件下载地址)

软件简介&#xff1a; 软件【下载地址】获取方式见文末。注&#xff1a;推荐使用&#xff0c;更贴合此安装方法&#xff01; 微软Office 2007是一款具有重大创新与革命性的办公软件套件。它引入了全新设计的用户界面&#xff0c;提供稳定安全的文件格式&#xff0c;并实现了无…

数据结构 - 堆(优先队列)+ 堆的应用 + 堆练习

文章目录 前言堆一、什么是堆二、堆又分为大根堆和小根堆三、由于堆的逻辑结构被看成完全二叉树&#xff0c;那么我们先来了解一下完全二叉树。四、堆使用数组还是链表储存数据呢&#xff1f;五、数组构建二叉树和父子节点之间的定位六、堆进行的操作七、实现小根堆1、堆的初始…