结构型模式--3.组合模式【草帽大船团】

2024-04-10 10:04

本文主要是介绍结构型模式--3.组合模式【草帽大船团】,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1. 好大一棵树

路飞在德雷斯罗萨打败多弗朗明哥之后,一些被路飞解救的海贼团自愿加入路飞麾下,自此组成了草帽大船团,旗下有7为船长,分别是:

俊美海贼团75人
巴托俱乐部56人
八宝水军1000人
艾迪欧海贼团4人
咚塔塔海贼团200人
巨兵海贼团5人
约塔玛利亚大船团4300人
小弟数量总计5640人。
在这里插入图片描述

对于草帽大船团的结构组成,很像一棵树:路飞是这棵树的根节点,旗下的七个船长是路飞的子节点。在这七个船长的旗下可能还有若干个船长。。。
在这里插入图片描述

像草帽大船团这样,能将多个对象组成一个树状结构,用以描述部分—整体的层次关系,使得用户对单个对象和组合对象的使用具有一致性,这样的结构性设计模式叫做组合模式

现实生活中能够和组合模式对应的场景也有很多,下面举例说明:

  1. Linux 的树状目录结构
  2. 国家的行政区划分(省级、地级、县级、乡级)
  3. 解放军编制(军、师、旅、团、营、连、排、班)
  4. 公司的组织结构(树状)

2. 大决战

在海贼中,大家都预测路飞的对手应该是同为四皇的黑胡子,黑胡子手下也有很多海贼船,双方一旦开战,必定死伤无数,最后的赢家就可以得到罗杰所留下的大秘宝ONE PIECE,并成为新的海贼王。

为了让路飞成为海贼王,我决定使用组合模式为路飞写一个管理草帽大船团的程序,其对应的主要操作是这样的:扩充船员、战斗减员、显示各船队信息、加入战斗等。

2.1 团队管理

对于组合模式来说,操作这个集合中的任意一个节点的方式都是相同的,所以必须要先定义出单个节点的抽象,在这个抽象类中定义出节点的行为。

// 抽象节点
class AbstractTeam
{
public:AbstractTeam(string name) :m_name(name) {}// 设置父节点void setParent(AbstractTeam* node){m_parent = node;}AbstractTeam* getParent(){return m_parent;}string getName(){return m_name;}virtual bool hasChild(){return false;}virtual void add(AbstractTeam* node) {}virtual void remove(AbstractTeam* node) {}virtual void fight() = 0;virtual void display() = 0;virtual ~AbstractTeam() {}
protected:string m_name;AbstractTeam* m_parent = nullptr;
};

草帽大船团中有若干个番队,这个抽象类对应的就是以船为单位的一个团队(一艘船就是一个节点),它内部定义了如下方法:

  1. 设置和获得当前船队的名字
    • 设置名字:构造函数
    • 获得名字:getName()
  2. 设置和得到当前船队节点的父节点
    • 设置父节点:setParent(AbstractTeam* node)
    • 得到父节点:getParent()
  3. 给当前番队添加一个子船队节点:add(AbstractTeam* node)
  4. 跟当前番队删除一个子船队节点:remove(AbstractTeam* node)
  5. 当前番队和敌人战斗:fight()
  6. 显示当前番队的信息:display()

2.2 叶子节点

草帽大船团是一种组合模式,也就是一种树状结构,在最末端的节点就没有子节点了,这种节点可以将其称之为叶子节点。叶子节点也是一个船队,所以它肯定是需要继承抽象节点类的。

// 叶子节点的小队
class LeafTeam : public AbstractTeam
{
public:using AbstractTeam::AbstractTeam;void fight() override{cout << m_parent->getName() + m_name + "与黑胡子的船员进行近距离肉搏战..." << endl;}void display() override{cout << "我是" << m_parent->getName() << "下属的" << m_name << endl;}~LeafTeam(){cout << "我是" << m_parent->getName() << "下属的" << m_name << ", 战斗已经结束, 拜拜..." << endl;}
};

叶子节点对应的番队由于没有子节点,所以在其对应的类中就不需要重写父类的add(AbstractTeam* node)remove(AbstractTeam* node)方法了,这也是基类中为什么不把这两个虚函数指定为纯虚函数的原因。

2.3 管理者节点

所谓的管理者节点其实就是非叶子节点。这种节点还拥有子节点,它的实现肯定是需要继承抽象节点类的。

// 管理者节点
class ManagerTeam : public AbstractTeam
{
public:using AbstractTeam::AbstractTeam;void fight() override{cout << m_name + "和黑胡子的恶魔果实能力者战斗!!!" << endl;}void add(AbstractTeam* node) override{node->setParent(this);m_children.push_back(node);}void remove(AbstractTeam* node) override{node->setParent(nullptr);m_children.remove(node);}bool hasChild(){return true;}list<AbstractTeam*> getChildren(){return m_children;}void display(){string info = string();for (const auto item : m_children){if (item == m_children.back()){info += item->getName();}else{// 优先级: + > +=info += item->getName() + ", ";}}cout << m_name + "的船队是【" << info << "】" << endl;}~ManagerTeam(){cout << "我是【" << m_name << "】战斗结束, 拜拜..." << endl;}
private:list<AbstractTeam*> m_children;
};

在管理者节点类的内部有一个容器list,容器内存储的就是它的子节点对象:

通过add(AbstractTeam* node)把当前番队的子节点存储到list
通过remove(AbstractTeam* node)把某一个子节点从当前番队的list中删除
通过display()来遍历这个list容器中的节点

2.4 战斗

最后把测试程序写一下:

// 内存释放
void gameover(AbstractTeam* root)
{if (root == nullptr){return;}if (root && root->hasChild()){ManagerTeam* team = dynamic_cast<ManagerTeam*>(root);list<AbstractTeam*> children = team->getChildren();for (const auto item : children){gameover(item);}}delete root;
}// 和黑胡子战斗
void fighting()
{vector<string> nameList = {"俊美海贼团", "巴托俱乐部", "八宝水军", "艾迪欧海贼团","咚塔塔海贼团", "巨兵海贼团", "约塔玛利亚大船团"};// 根节点ManagerTeam* root = new ManagerTeam("草帽海贼团");for (int i = 0; i < nameList.size(); ++i){ManagerTeam* child = new ManagerTeam(nameList.at(i));root->add(child);if (i == nameList.size() - 1){// 给最后一个番队添加子船队for (int j = 0; j < 9; ++j){LeafTeam* leaf = new LeafTeam("第" + to_string(j + 1) + "番队");child->add(leaf);leaf->fight();leaf->display();}child->fight();child->display();}}root->fight();root->display();cout << "====================================" << endl;gameover(root);
}int main()
{fighting();return 0;
}

输出的结果为:

约塔玛利亚大船团第1番队与黑胡子的船员进行近距离肉搏战...
我是约塔玛利亚大船团下属的第1番队
约塔玛利亚大船团第2番队与黑胡子的船员进行近距离肉搏战...
我是约塔玛利亚大船团下属的第2番队
约塔玛利亚大船团第3番队与黑胡子的船员进行近距离肉搏战...
我是约塔玛利亚大船团下属的第3番队
约塔玛利亚大船团第4番队与黑胡子的船员进行近距离肉搏战...
我是约塔玛利亚大船团下属的第4番队
约塔玛利亚大船团第5番队与黑胡子的船员进行近距离肉搏战...
我是约塔玛利亚大船团下属的第5番队
约塔玛利亚大船团第6番队与黑胡子的船员进行近距离肉搏战...
我是约塔玛利亚大船团下属的第6番队
约塔玛利亚大船团第7番队与黑胡子的船员进行近距离肉搏战...
我是约塔玛利亚大船团下属的第7番队
约塔玛利亚大船团第8番队与黑胡子的船员进行近距离肉搏战...
我是约塔玛利亚大船团下属的第8番队
约塔玛利亚大船团第9番队与黑胡子的船员进行近距离肉搏战...
我是约塔玛利亚大船团下属的第9番队
约塔玛利亚大船团和黑胡子的恶魔果实能力者战斗!!!
约塔玛利亚大船团的船队是【第1番队, 第2番队, 第3番队, 第4番队, 第5番队, 第6番队, 第7番队, 第8番队, 第9番队】
草帽海贼团和黑胡子的恶魔果实能力者战斗!!!
草帽海贼团的船队是【俊美海贼团, 巴托俱乐部, 八宝水军, 艾迪欧海贼团, 咚塔塔海贼团, 巨兵海贼团, 约塔玛利亚大船团】
====================================
我是【俊美海贼团】战斗结束, 拜拜...
我是【巴托俱乐部】战斗结束, 拜拜...
我是【八宝水军】战斗结束, 拜拜...
我是【艾迪欧海贼团】战斗结束, 拜拜...
我是【咚塔塔海贼团】战斗结束, 拜拜...
我是【巨兵海贼团】战斗结束, 拜拜...
我是约塔玛利亚大船团下属的第1番队, 战斗已经结束, 拜拜...
我是约塔玛利亚大船团下属的第2番队, 战斗已经结束, 拜拜...
我是约塔玛利亚大船团下属的第3番队, 战斗已经结束, 拜拜...
我是约塔玛利亚大船团下属的第4番队, 战斗已经结束, 拜拜...
我是约塔玛利亚大船团下属的第5番队, 战斗已经结束, 拜拜...
我是约塔玛利亚大船团下属的第6番队, 战斗已经结束, 拜拜...
我是约塔玛利亚大船团下属的第7番队, 战斗已经结束, 拜拜...
我是约塔玛利亚大船团下属的第8番队, 战斗已经结束, 拜拜...
我是约塔玛利亚大船团下属的第9番队, 战斗已经结束, 拜拜...
我是【约塔玛利亚大船团】战斗结束, 拜拜...
我是【草帽海贼团】战斗结束, 拜拜...

由于草帽大船团对应的设计模式是组合模式,它对应的是一个树模型,并且每个节点的操作方式都形同,所以在释放节点的时候就可以使用递归了,gameover()函数就是一个递归函数

3. 结构图

学完了组合模式,根据上面的例子把对应的UML类图画一下(学会之后就得先画类图,再写程序了
在这里插入图片描述

为了能够更加清楚地描述出设计模式中的组合关系(不是UML中的组合关系),在AbstractTeamManagerTeam之间画了两条线:

  • 继承关系:对节点的操作使用的是抽象类中提供的接口,以保证操作的一致性
  • 聚合关系:ManagerTeam类型的节点还可以有子节点,父节点和子节点的之间的关系需要具体问题具体分析
    • 子节点跟随父节点一起销毁,二者就是组合关系(UML中的组合关系)
    • 子节点不跟随父节点一起销毁,二者就是聚合关系
    • 上面的程序中,在父节点的析构函数中没有销毁它管理的子节点,所以在上图中标记的是聚合关系

这篇关于结构型模式--3.组合模式【草帽大船团】的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/890750

相关文章

Java设计模式---迭代器模式(Iterator)解读

《Java设计模式---迭代器模式(Iterator)解读》:本文主要介绍Java设计模式---迭代器模式(Iterator),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,... 目录1、迭代器(Iterator)1.1、结构1.2、常用方法1.3、本质1、解耦集合与遍历逻辑2、统一

Java 线程安全与 volatile与单例模式问题及解决方案

《Java线程安全与volatile与单例模式问题及解决方案》文章主要讲解线程安全问题的五个成因(调度随机、变量修改、非原子操作、内存可见性、指令重排序)及解决方案,强调使用volatile关键字... 目录什么是线程安全线程安全问题的产生与解决方案线程的调度是随机的多个线程对同一个变量进行修改线程的修改操

Redis Cluster模式配置

《RedisCluster模式配置》:本文主要介绍RedisCluster模式配置,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录分片 一、分片的本质与核心价值二、分片实现方案对比 ‌三、分片算法详解1. ‌范围分片(顺序分片)‌2. ‌哈希分片3. ‌虚

RabbitMQ工作模式中的RPC通信模式详解

《RabbitMQ工作模式中的RPC通信模式详解》在RabbitMQ中,RPC模式通过消息队列实现远程调用功能,这篇文章给大家介绍RabbitMQ工作模式之RPC通信模式,感兴趣的朋友一起看看吧... 目录RPC通信模式概述工作流程代码案例引入依赖常量类编写客户端代码编写服务端代码RPC通信模式概述在R

SQL Server身份验证模式步骤和示例代码

《SQLServer身份验证模式步骤和示例代码》SQLServer是一个广泛使用的关系数据库管理系统,通常使用两种身份验证模式:Windows身份验证和SQLServer身份验证,本文将详细介绍身份... 目录身份验证方式的概念更改身份验证方式的步骤方法一:使用SQL Server Management S

Redis高可用-主从复制、哨兵模式与集群模式详解

《Redis高可用-主从复制、哨兵模式与集群模式详解》:本文主要介绍Redis高可用-主从复制、哨兵模式与集群模式的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝... 目录Redis高可用-主从复制、哨兵模式与集群模式概要一、主从复制(Master-Slave Repli

一文带你搞懂Redis Stream的6种消息处理模式

《一文带你搞懂RedisStream的6种消息处理模式》Redis5.0版本引入的Stream数据类型,为Redis生态带来了强大而灵活的消息队列功能,本文将为大家详细介绍RedisStream的6... 目录1. 简单消费模式(Simple Consumption)基本概念核心命令实现示例使用场景优缺点2

Nginx location匹配模式与规则详解

《Nginxlocation匹配模式与规则详解》:本文主要介绍Nginxlocation匹配模式与规则,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、环境二、匹配模式1. 精准模式2. 前缀模式(不继续匹配正则)3. 前缀模式(继续匹配正则)4. 正则模式(大

Linux系统配置NAT网络模式的详细步骤(附图文)

《Linux系统配置NAT网络模式的详细步骤(附图文)》本文详细指导如何在VMware环境下配置NAT网络模式,包括设置主机和虚拟机的IP地址、网关,以及针对Linux和Windows系统的具体步骤,... 目录一、配置NAT网络模式二、设置虚拟机交换机网关2.1 打开虚拟机2.2 管理员授权2.3 设置子

SpringBoot如何通过Map实现策略模式

《SpringBoot如何通过Map实现策略模式》策略模式是一种行为设计模式,它允许在运行时选择算法的行为,在Spring框架中,我们可以利用@Resource注解和Map集合来优雅地实现策略模式,这... 目录前言底层机制解析Spring的集合类型自动装配@Resource注解的行为实现原理使用直接使用M