当前位置: 首页 > news >正文

结构型模式--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.组合模式【草帽大船团】

1. 好大一棵树 路飞在德雷斯罗萨打败多弗朗明哥之后&#xff0c;一些被路飞解救的海贼团自愿加入路飞麾下&#xff0c;自此组成了草帽大船团&#xff0c;旗下有7为船长&#xff0c;分别是&#xff1a; 俊美海贼团75人 巴托俱乐部56人 八宝水军1000人 艾迪欧海贼团4人 咚塔塔海…...

网络基础三——其他周边问题

3.1ARP原理 ​ ARP不是一个单纯的数据链路层的协议&#xff0c;而是一个介于数据链路层和网络层之间的协议&#xff1b; ​ 以广播的形式(主机号填成全1)构建Mac帧&#xff0c;发送ARP请求包&#xff0c;告诉所有在局域网的主机我的IP地址和Mac帧&#xff0c;与目的IP相同的主…...

学习周报:文献阅读+Fluent案例+水力学理论学习

目录 摘要 Abstract 文献阅读&#xff1a;物理信息的神经网络与湍流传质的非封闭机制模型相结合 文献摘要 提出问题 提出方案 实验设置 所需方程介绍 雷诺时均方程&#xff08;RANS&#xff09; K-epsilon两方程模型 神经网络框架 DNN部分 损失函数定义 PINN部分…...

Redis(持久化 -- RDB AOF)

持久化 通常我们认为持久化为: 重启进程/重启主机之后, 数据仍然存在不丢失 把数据存储在硬盘上 – 持久 把数据存储在内存中 – 不持久 Redis 持久化 redis 是一个内存数据库, 也就是说本身是不持久的(但是快[效率高]), 于是 Redis 提供了持久化机制 — RDB 和 AOF 二者都是对…...

LDR6328助力Type-C普及,便捷充电,绿色生活更精彩

随着科技的进步和全球统一接口的需求&#xff0c;Type-C接口正日益受到青睐。越来越多的设备正选择采纳这一先进的接口设计&#xff0c;它的普及无疑在改善着我们的日常生活。 在过往&#xff0c;许多小功率设备如小风扇、蓝牙音箱、桌面台灯以及家用加湿器等&#xff0c;都普遍…...

redis主从复制、哨兵模式、集群

文章目录 redis主从复制主从复制的配置**安装Redis**配置主服务器配置从服务器验证主从效果 哨兵模式哨兵的工作机制哨兵模式的搭建启动哨兵 集群分布式集群的搭建 redis主从复制 Redis主从复制&#xff08;Redis replication&#xff09;是Redis提供的一种数据备份和故障转移…...

shell免登陆脚本

#!/bin/bash ## 脚本接收的参数&#xff0c;也就是要互相配置 SSH 免密登录的服务器列表参数 BASE_HOST_LISTip ## 密码&#xff0c;默认用户是当前运行脚本的用户&#xff0c;比如 root 用户 ## 这里改成你的用户对应的密码 BASE_PASSWORD"password" ## shell 函…...

基于springboot+vue+Mysql的职称评审管理系统

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…...

GitLab教程(一):安装Git、配置SSH公钥

文章目录 序一、Git安装与基本配置&#xff08;Windows&#xff09;下载卸载安装基本配置 二、SSH密钥配置 序 为什么要使用代码版本管理工具&#xff1a; 最近笔者确实因为未使用代码版本管理工具遇到了一些愚蠢的问题&#xff0c;笔者因此认为代码版本管理工具对于提高团队…...

【算法】无序数组的两数之和 - map标记

题目 在一个无序数组中找到两个数&#xff0c;两个数之和为给定的一个数&#xff0c;返回两个数在数组中的下标。 原理 遍历数组&#xff0c;遍历到一个数字的时候&#xff0c;记录下这个数及其下标&#xff0c;遍历时判断给定数减去这个数为key在map中是否存在&#xff0c;…...

Prime (2021): 2

前言 这个靶机有亿点难,收获很多。打靶的时候&#xff0c;前面很顺&#xff0c;到创建ssh公钥之后就一点不会了。 1 01 arp扫描&#xff0c;发现有一个130&#xff0c;再查看端口 有22&#xff0c;80&#xff0c;129&#xff0c;445&#xff0c;10123 dirb扫描目录 这…...

React 状态管理:安全高效地修改对象属性的 3 种方法

在 React 应用程序中,状态(state)是驱动整个应用程序的核心。当应用程序的状态发生变化时,React 会自动重新渲染相应的组件,以确保用户界面的更新。 与数组状态一样,对象状态在 React 中也需要特别处理。直接修改对象属性是不被允许的,因为 React 的不可变性原则要求我们创建一…...

python实现pdf的页面替换

利用第三方库PyPDF2&#xff0c;下面例子中进行的是将 origin.pdf 的第17页替换为 s17.pdf 的第1页&#xff1a; import PyPDF2def replace_pages(original_pdf_path, replacement_pages):with open(original_pdf_path, rb) as original_file:original_pdf PyPDF2.PdfReader(…...

[AIGC] Java List和Map常用API以及其Python实现方式对照介绍

Java和Python作为当今非常浅显易懂的编程语言&#xff0c;其数据结构中对于List和Map&#xff08;Java&#xff09;或List和Dict&#xff08;Python&#xff09;的操作无疑是每个程序员都非常必需的知识。本文将介绍在Java中对List和Map常用的一些操作&#xff0c;并给出在Pyth…...

零基础如何闯入IT的神秘大门?

前言 随着信息技术的飞速发展&#xff0c;IT行业成为了许多有志之士梦寐以求的职业领域。但对于零基础的人来说&#xff0c;如何成功进入这个行业却是一个不小的挑战。下面&#xff0c;我将结合自身的C语言专业知识&#xff0c;为大家详细阐述一条可行的学习路径&#xff0c;并…...

java程序 .exe启动nginx防止重复启动,已解决

java代码生成好的.exe启动nginx服务程序 根据nginx占用端口来解决nginx服务重复启动问题&#xff08;下面代码了解代码逻辑后根据自己的业务需求修改即可&#xff09; 代码&#xff1a; package org.example;import javax.swing.*; import java.awt.*; import java.io.*; …...

二十一、Rust 反射 获取类型

不同于 java 中的反射&#xff0c;Rust 没有提供以往意义上的运行时反射&#xff0c;取而代之的是 “编译期反射”&#xff0c;如 类型分析、类型转换、类型签名。但即便如此&#xff0c;也已经能对 Rust元编程 提供很多助力了。 这种操作&#xff0c;主要通过 Any 来实现&…...

Flutter Engine引擎概念

1.Flutter是Google提供的开源框架。 2.本身由C编写并兼容iOS(底层C)/Android(底层C)平台的FlutterEngine框架负责UI渲染、数据转移、调用DartVM虚拟机。 3.FlutterEngine框架由Skia图形库、Dart运行时、Flutter框架代码组成。Skia是用于图形绘制和文本显示的2D图形引擎库&#…...

【运行环境】加载资源的形式

相关资源&#xff1a;性能优化原则 1 加载资源的形式 html代码 媒体文件&#xff0c;如图片&#xff0c;视频等 javasccript css 2 加载资源的过程 DNS解析&#xff1a;域名-> ip地址 浏览器根据IP地址向服务器发送http 请求 服务器处理http 请求&#xff0c;并返回给浏览器…...

备战蓝桥杯Day40 - 第11届python组真题 - C跑步锻炼

一、题目描述 二、思路 1、使用datetime库中的方法可以很好的解决这个问题。 2、定义起始时间和结束时间&#xff0c;判断是否是周一或者是1号&#xff0c;结果res加上相应的里程数。 3、最后输出 res 即为本题答案。 三、代码实现 import datetimestart datetime.date(2…...

微信网页版访问终极指南:wechat-need-web插件完整教程

微信网页版访问终极指南&#xff1a;wechat-need-web插件完整教程 【免费下载链接】wechat-need-web 让微信网页版可用 / Allow the use of WeChat via webpage access 项目地址: https://gitcode.com/gh_mirrors/we/wechat-need-web 还在为无法在浏览器中使用微信网页版…...

用AI工具做技术课程:一个人完成录课、剪辑、上架全流程

软件测试从业者的知识变现新路径作为一名软件测试工程师&#xff0c;你手里握着大量值钱的东西——接口自动化怎么搭、性能瓶颈怎么定位、测试用例怎么设计才不漏测。这些东西在你的团队里可能是常识&#xff0c;但放到整个行业&#xff0c;就是别人愿意付费学习的硬通货。但一…...

嵌入式音频处理与SD卡系统克隆实战指南

1. 项目概述与核心价值如果你正在捣鼓一块像Chumby Hacker Board这样的嵌入式开发板&#xff0c;或者任何带有音频输出和SD卡存储的Linux设备&#xff0c;那么你迟早会碰到两个绕不开的“硬骨头”&#xff1a;音频信号的处理和存储系统的克隆部署。前者决定了你的设备能不能“好…...

Vibeproxy:轻量级可编程HTTP代理,实现API Mock与故障注入

1. 项目概述&#xff1a;一个轻量级的HTTP代理工具最近在折腾一些需要模拟不同网络环境或者进行API测试的项目时&#xff0c;我一直在寻找一个足够轻量、灵活且易于集成的HTTP代理工具。市面上成熟的代理方案很多&#xff0c;但要么功能过于臃肿&#xff0c;要么配置起来相当繁…...

Linux进程诊断利器whatdiditdo:实时快照工具原理与实战

1. 项目概述&#xff1a;一个“透明”的进程监控器最近在折腾一个自动化脚本&#xff0c;它时不时会卡住&#xff0c;但日志里又看不出个所以然。排查这种问题&#xff0c;最直接的想法就是看看这个进程到底在“干什么”——它在读写哪些文件&#xff1f;调用了哪些系统调用&am…...

量子密钥分发在电力SCADA系统中的应用与协议对比

1. 量子密钥分发在电力SCADA系统中的关键作用电力系统的网络安全防护正面临前所未有的挑战。作为国家关键基础设施的核心&#xff0c;电力SCADA系统每天处理着海量的实时监测与控制数据&#xff0c;这些数据的机密性和完整性直接关系到电网的安全运行。传统加密技术如RSA和AES虽…...

SPI驱动NeoPixel:硬件时序优化与跨平台控制方案

1. 项目概述&#xff1a;当NeoPixel遇上SPI&#xff0c;一个关于时序的优雅解法玩过智能LED&#xff0c;比如Adafruit的NeoPixel或者国内常见的WS2812B灯带的朋友&#xff0c;大概都体会过那种又爱又恨的感觉。爱的是它单线控制、色彩绚烂&#xff0c;恨的是那娇贵到令人头疼的…...

AI建站工具选型指南:一张表看懂怎么选,哪个适合你

AI建站工具选型指南&#xff1a;一张表看懂怎么选&#xff0c;哪个适合你痛点与目标&#xff1a;为什么选个工具这么难市面上的建站工具都宣传自己能“AI生成”“一键建站”&#xff0c;但你点进去一看&#xff0c;有的要自己拖模板&#xff0c;有的要自己写文案&#xff0c;有…...

VMware Workstation 17 Pro 保姆级教程:5分钟搞定Win11虚拟机TPM 2.0和安全启动配置

VMware Workstation 17 Pro 极速配置指南&#xff1a;Win11虚拟机TPM 2.0与安全启动实战 在虚拟化技术领域&#xff0c;VMware Workstation一直保持着领先地位。随着Windows 11的发布&#xff0c;许多开发者和技术爱好者都希望在虚拟机中体验这个新系统&#xff0c;却频繁遭遇T…...

初创团队如何借助 Taotoken 的 Token Plan 有效控制大模型使用成本

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 初创团队如何借助 Taotoken 的 Token Plan 有效控制大模型使用成本 对于初创团队和独立开发者而言&#xff0c;在项目早期验证想法…...