设计模式:组合模式(C#、JAVA、JavaScript、C++、Python、Go、PHP)
上一篇《模板模式》 下一篇《代理模式》
简介:
组合模式,它是一种用于处理树形结构、表示“部分-整体”层次结构的设计模式。它允许你将对象组合成树形结构,以表示部分和整体的关系。这种模式的主要目的是简化客户端代码,并使客户端以一致的方式处理单个对象和组合对象。
在组合模式中,抽象根节点(Component)定义了系统各层次对象的公有方法和属性,可以预先定义一些默认行为和属性。树枝节点(Composite)定义了树枝节点的行为,存储子节点,组合树枝节点和叶子节点形成一个树形结构。叶子节点(Leaf)是系统层次遍历的最小单位。
组合模式的使用场景包括但不限于以下几种情况:
1、表示部分-整体层次结构:组合模式适用于需要处理部分-整体关系的场景,例如文件系统中的文件夹和文件之间的关系。通过组合模式,可以将文件夹和文件组合在一起,形成一个树形结构,方便用户进行操作和管理。
2、处理树形结构:组合模式适用于处理树形结构的情况,例如表达式树、决策树等。在这些场景中,可以将对象组合成树形结构,以表示部分和整体的关系,方便对整个树形结构进行操作和管理。
3、简化客户端代码:组合模式可以简化客户端代码,使得客户端只需要面对一致的对象而不用考虑整体部分或叶子节点的问题。通过组合模式,客户端可以将对组合对象的操作委托给其子对象,而无需知道具体执行的是单个对象还是整个组合。
4、提高系统的灵活性和可扩展性:组合模式可以使得系统的设计更加灵活和可扩展。通过组合模式,可以轻松地添加新的对象类型,而不需要修改现有的代码。同时,组合模式还可以使得系统更容易适应变化和扩展,提高系统的可维护性和可重用性。
总之,组合模式适用于需要处理部分-整体关系的场景,以及需要简化客户端代码、提高系统灵活性和可扩展性的情况。在使用组合模式时,需要注意抽象化、封装和继承等问题,以保证代码的正确性和可维护性。
组合模式的创建步骤如下:
1、创建抽象组件类(Component),该类通常包含一些共有的属性和方法,例如添加、删除子节点等。
2、创建具体组件类(Leaf),该类继承自抽象组件类,并实现具体的业务逻辑。
3、创建复合组件类(Composite),该类同样继承自抽象组件类,并添加对子节点的管理操作,例如添加、删除子节点等。
4、在复合组件类中实现一些公共操作,例如遍历整个树形结构等。
5、客户端代码通过调用复合组件类的操作来管理整个树形结构,而无需关心具体的叶子节点或复合组件类的内部实现细节。
需要注意的是,在实际应用中,需要根据具体需求选择是否使用组合模式,并注意处理好对象组合的层次关系,以及保证代码的正确性和可维护性。
组合模式的优点,主要包括:
1、清楚地定义分层次的复杂对象,表示对象的全部或部分层次。这使得增加新构件也更容易,因为可以在已有的层次结构中添加新的节点。
2、客户端调用简单。客户端可以一致地使用组合结构或其中单个对象,而不必关心处理的是单个对象还是整个组合结构,这简化了客户端代码。
3、更容易在组合体内加入对象构件。在组合模式中,增加新的容器构件和叶子构件都很方便,无需对现有类库进行任何修改,符合"开闭原则"。
4、为树型结构的面向对象实现提供了一种灵活的解决方案。通过叶子对象和容器对象的递归组合,可以形成复杂的树型结构,但对树型结构的控制却非常简单。
总的来说,组合模式能够简化客户端代码,并使得增加新构件和形成复杂树形结构更加容易。同时,它还提供了一种灵活的解决方案,使得面向对象实现更加高效。
组合模式的缺点,主要包括:
1、设计较复杂。由于组合模式需要定义抽象组件类、具体组件类和复合组件类,以及它们之间的层次关系和操作,因此设计起来相对复杂。
2、不容易限制容器中的构件。在组合模式中,容器中的构件可以自由地添加、删除和组合,这可能导致一些问题,例如无限递归、内存泄漏等。
3、不容易用继承的方法来增加构件的新功能。由于组合模式采用递归组合的方式,而不是通过继承来增加构件的新功能,因此需要手动添加新的构件,这可能会增加代码的复杂度和维护成本。
示例:
一、C#组合模式
以下是一个示例,展示了如何在C#中实现组合模式:
首先,定义一个抽象组件类(Component),该类通常包含一些共有的属性和方法,例如添加、删除子节点等。
public abstract class Component
{ public virtual void Operation() { // 具体实现留给子类去完成 }
}
然后,创建具体组件类(Leaf),该类继承自抽象组件类,并实现具体的业务逻辑。
public class Leaf : Component
{ public override void Operation() { // 具体实现留给子类去完成 }
}
接下来,创建复合组件类(Composite),该类同样继承自抽象组件类,并添加对子节点的管理操作,例如添加、删除子节点等。
public class Composite : Component, ICollection<Component>
{ private List<Component> children = new List<Component>(); public void Add(Component component) { children.Add(component); } public void Remove(Component component) { children.Remove(component); } public override void Operation() { foreach (var child in children) { child.Operation(); // 递归调用子节点的Operation方法 } }
}
最后,在客户端代码中,可以通过创建复合组件类的实例来管理整个树形结构,而无需关心具体的叶子节点或复合组件类的内部实现细节。
public class Client { public void test() { Composite root = new Composite(); // 创建根节点 Leaf leaf1 = new Leaf(); // 创建叶子节点1 Leaf leaf2 = new Leaf(); // 创建叶子节点2 Composite composite1 = new Composite(); // 创建复合节点1 Composite composite2 = new Composite(); // 创建复合节点2 // 将节点添加到相应的复合节点中,或者直接添加到根节点下 root.Add(leaf1); root.Add(leaf2); composite1.Add(leaf1); composite2.Add(leaf2); root.Add(composite1); root.Add(composite2); // 调用根节点的Operation方法,将递归遍历整个树形结构并执行相应的操作 root.Operation();}
}
二、java组合模式
组合模式通常通过以下方式实现:
//首先,定义一个抽象组件类(Component),该类通常包含一些共有的属性和方法,例如添加、删除子节点等。
public abstract class Component { public void add(Component component) { // 具体实现留给子类去完成 } public void remove(Component component) { // 具体实现留给子类去完成 } public void operation() { // 具体实现留给子类去完成 }
}
//然后,创建具体组件类(Leaf),该类继承自抽象组件类,并实现具体的业务逻辑。
public class Leaf extends Component { @Override public void operation() { // 具体实现留给子类去完成 }
}
//接下来,创建复合组件类(Composite),该类同样继承自抽象组件类,并添加对子节点的管理操作,例如添加、删除子节点等。
public class Composite extends Component { private List<Component> children = new ArrayList<>(); @Override public void add(Component component) { children.add(component); } @Override public void remove(Component component) { children.remove(component); } @Override public void operation() { for (Component child : children) { child.operation(); // 递归调用子节点的operation方法 } }
}
//最后,在客户端代码中,可以通过创建复合组件类的实例来管理整个树形结构,而无需关心具体的叶子节点或复合组件类的内部实现细节。
public class Client { public static void main(String[] args) {Composite root = new Composite(); // 创建根节点 Leaf leaf1 = new Leaf(); // 创建叶子节点1 Leaf leaf2 = new Leaf(); // 创建叶子节点2 Composite composite1 = new Composite(); // 创建复合节点1 Composite composite2 = new Composite(); // 创建复合节点2 // 将节点添加到相应的复合节点中,或者直接添加到根节点下 root.add(leaf1); root.add(leaf2); composite1.add(leaf1); composite2.add(leaf2); root.add(composite1); root.add(composite2); // 调用根节点的operation方法,将递归遍历整个树形结构并执行相应的操作 root.operation();}
}
三、javascript组合模式
在JavaScript中,组合模式的实现方式如下:
//定义一个抽象组件类
class Component { constructor(name) { this.name = name; this.children = []; } addChild(component) { this.children.push(component); } removeChild(component) { this.children = this.children.filter(child => child !== component); } printTree() { console.log(this.name); this.children.forEach(child => child.printTree()); }
}
//创建具体组件类
class Leaf extends Component { constructor(name) { super(name); } printLeaf() { console.log(`${this.name} (Leaf)`); }
}
//创建复合组件类
class Composite extends Component { constructor(name) { super(name); } addChild(component) { if (component instanceof Leaf) { super.addChild(component); } else if (component instanceof Composite) { component.children.forEach(child => this.addChild(child)); } else { throw new Error('Invalid component type'); } }
}
四、C++组合模式
以下是在C++中实现组合模式:
#include <iostream> class Component {
public: virtual void operation() = 0;
}; class Leaf : public Component {
public: Leaf(int value) : m_value(value) {} void operation() override { std::cout << "Leaf operation: " << m_value << std::endl; }
private: int m_value;
}; class Composite : public Component {
public: Composite() {} void addChild(Component* child) { m_children.push_back(child); } void removeChild(Component* child) { m_children = m_children.erase(std::remove(m_children.begin(), m_children.end(), child)); } void operation() override { for (auto child : m_children) { child->operation(); } }
private: std::vector<Component*> m_children;
}; int main() { Composite* root = new Composite(); Leaf* leaf1 = new Leaf(1); Leaf* leaf2 = new Leaf(2); Composite* composite1 = new Composite(); Composite* composite2 = new Composite(); root->addChild(leaf1); root->addChild(leaf2); composite1->addChild(leaf1); composite2->addChild(leaf2); root->addChild(composite1); root->addChild(composite2); root->operation();
}
五、python组合模式
以下是在python中实现组合模式:
class Component: def operation(self): pass class Leaf(Component): def __init__(self, value): self.value = value def operation(self): print(f"Leaf operation: {self.value}") class Composite(Component): def __init__(self): self.children = [] def add_child(self, child): self.children.append(child) def remove_child(self, child): self.children = [c for c in self.children if c != child] def operation(self): for child in self.children: child.operation() def main(): root = Composite() leaf1 = Leaf(1) leaf2 = Leaf(2) composite1 = Composite() composite2 = Composite() root.add_child(leaf1) root.add_child(leaf2) composite1.add_child(leaf1) composite2.add_child(leaf2) root.add_child(composite1) root.add_child(composite2) root.operation() if __name__ == "__main__": main()
在这个示例代码中,定义了一个Component类作为抽象组件类,它有一个operation()方法,用于实现组件的操作。Leaf类和Composite类继承自Component类,分别表示叶节点和复合节点。Leaf类有一个value属性,表示节点的值,而Composite类有一个children属性,用于存储子节点。Composite类还实现了add_child()和remove_child()方法,用于添加和删除子节点。最后,在main()函数中创建了一个对象树,并调用operation()方法来执行操作。
六、go组合模式
以下是一个示例,展示了如何在go中实现组合模式:
package main import "fmt" type Component interface { Operation()
} type Leaf struct { value int
} func (l *Leaf) Operation() { fmt.Printf("Leaf operation: %d\n", l.value)
} type Composite struct { children []*Component
} func (c *Composite) Operation() { for _, child := range c.children { child.Operation() }
} func main() { root := &Composite{} leaf1 := &Leaf{value: 1} leaf2 := &Leaf{value: 2} composite1 := &Composite{} composite2 := &Composite{} root.children = []*Component{leaf1, leaf2, composite1, composite2} composite1.children = []*Component{leaf1} composite2.children = []*Component{leaf2} root.Operation()
}
在这个示例代码中,定义了一个Component接口,它有一个Operation()方法。Leaf和Composite结构体实现了Component接口。Leaf结构体有一个value属性,表示节点的值,而Composite结构体有一个children属性,用于存储子节点。Composite结构体还实现了Operation()方法,用于递归地调用子节点的Operation()方法。在main()函数中创建了一个对象树,并调用Operation()方法来执行操作。
七、PHP组合模式
以下是一个示例,展示了如何在PHP中实现组合模式:
<?php class Component { protected $name; protected $children; public function __construct($name) { $this->name = $name; $this->children = []; } public function addChild(Component $child) { $this->children[] = $child; } public function removeChild(Component $child) { $this->children = array_filter($this->children, function ($component) use ($child) { return $component !== $child; }); } public function getName() { return $this->name; }
} class Leaf extends Component { public function __construct($name) { parent::__construct($name); }
} class Composite extends Component { public function __construct($name) { parent::__construct($name); } public function getChildren() { return $this->children; }
} // 创建树形结构
$root = new Composite("Root");
$leaf1 = new Leaf("Leaf 1");
$leaf2 = new Leaf("Leaf 2");
$composite1 = new Composite("Composite 1");
$composite2 = new Composite("Composite 2");
$root->addChild($leaf1);
$root->addChild($leaf2);
$composite1->addChild($leaf1);
$composite2->addChild($leaf2);
$root->addChild($composite1);
$root->addChild($composite2); // 遍历树形结构并输出节点名称
function traverse(Component $node) { echo $node->getName() . " "; if ($node instanceof Composite) { foreach ($node->getChildren() as $child) { traverse($child); } }
}
traverse($root);
在这个示例中,Component类是抽象的根类,表示树中的节点。Leaf类表示叶节点,没有子节点;Composite类表示复合节点,可以包含子节点。通过addChild()和removeChild()方法,可以向复合节点添加或移除子节点。通过getChildren()方法,可以获取复合节点的子节点列表。最后,使用traverse()函数来遍历树形结构并输出节点名称。
《完结》
上一篇《模板模式》 下一篇《代理模式》
相关文章:

设计模式:组合模式(C#、JAVA、JavaScript、C++、Python、Go、PHP)
上一篇《模板模式》 下一篇《代理模式》 简介: 组合模式,它是一种用于处理树形结构、表示“部分-整体”层次结构的设计模式。它允许你将对象组合成树形结构,以表示部分…...

超强满血不收费的AI绘图教程来了(在线Stable Diffusion一键即用)
超强满血不收费的AI绘图教程来了(在线Stable Diffusion一键即用) 一、简介1.1 AI绘图1.2 Stable Diffusion1.2.1 原理简述1.2.2 应用流程 二、AI绘图工具2.1 吐司TusiArt2.2 哩布哩布LibLibAI2.3 原生部署 三、一键即用3.1 开箱尝鲜3.2 模型关联3.3 Cont…...

【蓝桥每日一题]-动态规划 (保姆级教程 篇12)#照相排列
这次是动态规划最后一期了,感谢大家一直以来的观看,以后就进入新的篇章了 目录 题目:照相排列 思路: 题目:照相排列 思路: 首先记录状态f[a][b][c][d][e]表示每排如此人数下对应的方案数,然…...

纺织工厂数字孪生3D可视化管理平台,推动纺织产业数字化转型
近年来,我国加快数字化发展战略部署,全面推进制造业数字化转型,促进数字经济与实体经济深度融合。以数字孪生、物联网、云计算、人工智能为代表的数字技术发挥重要作用。聚焦数字孪生智能工厂可视化平台,推动纺织制造业数字化转型…...

【七】SpringBoot为什么可以打成 jar包启动
SpringBoot为什么可以打成 jar包启动 简介:庆幸的是夜跑的习惯一直都在坚持,正如现在坚持写博客一样。最开始刚接触springboot的时候就觉得很神奇,当时也去研究了一番,今晚夜跑又想起来了这茬事,于是想着应该可以记录一…...

031-第三代软件开发-屏幕保护
第三代软件开发-屏幕保护 文章目录 第三代软件开发-屏幕保护项目介绍屏幕保护 关键字: Qt、 Qml、 MediaPlayer、 VideoOutput、 function 项目介绍 欢迎来到我们的 QML & C 项目!这个项目结合了 QML(Qt Meta-Object Language&#…...
Ubuntu 22.04 更新完内核重启卡在 grub 命令行解决办法
倒霉伊始 升级内核过程中出现如下警告,然后重启引导失败: Warning: os-prober will not be executed to detect other bootable partitions 屏幕内容如下: GNU GRUB version 2.06Minimal BASH-like line editing is supported. For the fir…...

Stream流式处理
Stream流式处理: 建立在Lambda表达式基础上的多数据处理技术。 可以对集合进行迭代、去重、筛选、排序、聚合等处理,极大的简化了代码量。 Stream常用方法 Stream流对象的五种创建方式 //基于数组 String[] arr {"a","b","c…...
ROG STRIX GS-AX5400 使用笔记
1. 技术支持 咨询京东(因为是在京东购买的) 2. 增强信号设置 Note:关于设置的具体步骤,请参考教程《华硕路由器地区如何改成澳大利亚》。 操作路径:打开主页<192.168.50.1> ⇒ 输入用户名和密码后选择登录 ⇒ …...

【刷题-PTA】堆栈模拟队列(代码+动态图解)
【刷题-PTA】堆栈模拟队列(代码动态图解) 文章目录 【刷题-PTA】堆栈模拟队列(代码动态图解)题目输入格式:输出格式:输入样例:输出样例: 分析题目区分两栈解题思路伪代码动图演示代码测试 题目 题目描述 : 设已知有两个堆栈S1和S2,请用这两个堆栈模拟出一个队列Q。 …...

FileUpload控件上传文件时出现 不支持给定路径的格式.的解决方法
正常代码,部署到server 2012时,在上传音频mp3文件时,显示错误“不支持给定路径的格式”,上传控件使用FileUpload控件: 因为程序之前是正常的,因此应该不是程序的问题。 上传时,发现在选择文件时…...
这两天的一些碎碎念
一直以来我都不算是一个非常热爱运维岗位的一个人,其实本行工作对于我来说只是一个工作。运维的广度很大,说什么工作了7年了,可最终总感觉还曾是一窍不通。 什么shell啊,什么python啊,什么大数据啊,7年里&a…...

Unity 最新DOTS系列之《Baking与Baker的详解》
Unity DOTS Baking与Baker详解 最近DOTS终于发布了正式的版本, 我们来分享一下DOTS里面Baking 与Baker的关键概念,方便大家上手学习掌握Unity DOTS开发。 对惹,这里有一个游戏开发交流小组,希望大家可以点击进来一起交流一下开发经验呀&…...

【Tensorflow 2.12 简单智能商城商品推荐系统搭建】
Tensorflow 2.12 简单智能商城商品推荐系统搭建 前言架构数据召回排序部署调用结尾 前言 基于 Tensorflow 2.12 搭建一个简单的智能商城商品推荐系统demo~ 主要包含6个部分,首先是简单介绍系统架构,接着是训练数据收集、处理,然后是召回模型、…...

Unity 单例-接口模式
单例-接口模式 使用接口方式实现的单例可以继承其它类,更加方便 using System.Collections; using System.Collections.Generic; using UniRx; using UniRx.Triggers; using UnityEngine; namespace ZYF {public interface ISingleton<TMono> where TMono : M…...

【Java 进阶篇】Java XML解析:从入门到精通
XML(可扩展标记语言)是一种常用的数据格式,用于存储和交换数据。在Java中,XML解析是一项重要的任务,它允许您从XML文档中提取和操作数据。本篇博客将从基础开始,详细介绍如何在Java中解析XML文档࿰…...
【图像配准】Canny边缘检测+模板配准红外可见光双路数据
研究目的 最近在做无人机遥感红外和可见光双路数据配准,由于红外相机视野范围较小,因此配准的目的主要是在可见光的视野范围内,裁剪出红外图像对应的部分,同时,保持可见光的高分辨率不变。 本文思路 本文尝试使用Ca…...
关于单机流程编排技术——docker compose安装使用的问题
最近在学习docker相关的东西,当我在docker上部署了一个nest应用,其中该应用中依赖了一个基于mysql镜像的容器,一个基于redis镜像的容器。那我,当我进行部署上线时,在启动nest容器时,必须保证redis容器和mys…...

Google Chrome的新“IP保护”功能将隐藏用户的IP地址
导语:在保护用户隐私方面,Google Chrome正在测试一项名为“IP保护”的新功能。通过使用代理服务器掩盖用户的IP地址,这项功能能够增强用户的隐私保护。在意识到IP地址可能被用于秘密追踪后,Google希望在确保用户隐私的同时&#x…...

做机器视觉工程师,苏州德创能不能去工作?
每一家公司都有自身特点,同时也每一家都有自身的bug。 苏州德创作为美国康耐视Cognex产品在华东最大的代理商,也是康耐视外包团队。那么苏州德创有哪些业务构成,业务的构成也是其招聘的主要人员的方向。 设备视觉供应商,如卓越&…...
谷歌浏览器插件
项目中有时候会用到插件 sync-cookie-extension1.0.0:开发环境同步测试 cookie 至 localhost,便于本地请求服务携带 cookie 参考地址:https://juejin.cn/post/7139354571712757767 里面有源码下载下来,加在到扩展即可使用FeHelp…...
【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密
在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...
Axios请求超时重发机制
Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式: 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...

IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)
文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...

智能仓储的未来:自动化、AI与数据分析如何重塑物流中心
当仓库学会“思考”,物流的终极形态正在诞生 想象这样的场景: 凌晨3点,某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径;AI视觉系统在0.1秒内扫描包裹信息;数字孪生平台正模拟次日峰值流量压力…...
Element Plus 表单(el-form)中关于正整数输入的校验规则
目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入(联动)2.1 模板2.2 校验规则2.3 CSS 1 单个正整数输入 1.1 模板 <el-formref"formRef":model"formData":rules"formRules"label-width"150px"…...

使用 SymPy 进行向量和矩阵的高级操作
在科学计算和工程领域,向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能,能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作,并通过具体…...
Typeerror: cannot read properties of undefined (reading ‘XXX‘)
最近需要在离线机器上运行软件,所以得把软件用docker打包起来,大部分功能都没问题,出了一个奇怪的事情。同样的代码,在本机上用vscode可以运行起来,但是打包之后在docker里出现了问题。使用的是dialog组件,…...
Python ROS2【机器人中间件框架】 简介
销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...
CSS设置元素的宽度根据其内容自动调整
width: fit-content 是 CSS 中的一个属性值,用于设置元素的宽度根据其内容自动调整,确保宽度刚好容纳内容而不会超出。 效果对比 默认情况(width: auto): 块级元素(如 <div>)会占满父容器…...