设计模式:组合模式(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产品在华东最大的代理商,也是康耐视外包团队。那么苏州德创有哪些业务构成,业务的构成也是其招聘的主要人员的方向。 设备视觉供应商,如卓越&…...
HTML 语义化
目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案: 语义化标签: <header>:页头<nav>:导航<main>:主要内容<article>&#x…...
基于当前项目通过npm包形式暴露公共组件
1.package.sjon文件配置 其中xh-flowable就是暴露出去的npm包名 2.创建tpyes文件夹,并新增内容 3.创建package文件夹...
Map相关知识
数据结构 二叉树 二叉树,顾名思义,每个节点最多有两个“叉”,也就是两个子节点,分别是左子 节点和右子节点。不过,二叉树并不要求每个节点都有两个子节点,有的节点只 有左子节点,有的节点只有…...
Windows安装Miniconda
一、下载 https://www.anaconda.com/download/success 二、安装 三、配置镜像源 Anaconda/Miniconda pip 配置清华镜像源_anaconda配置清华源-CSDN博客 四、常用操作命令 Anaconda/Miniconda 基本操作命令_miniconda创建环境命令-CSDN博客...
BLEU评分:机器翻译质量评估的黄金标准
BLEU评分:机器翻译质量评估的黄金标准 1. 引言 在自然语言处理(NLP)领域,衡量一个机器翻译模型的性能至关重要。BLEU (Bilingual Evaluation Understudy) 作为一种自动化评估指标,自2002年由IBM的Kishore Papineni等人提出以来,…...
毫米波雷达基础理论(3D+4D)
3D、4D毫米波雷达基础知识及厂商选型 PreView : https://mp.weixin.qq.com/s/bQkju4r6med7I3TBGJI_bQ 1. FMCW毫米波雷达基础知识 主要参考博文: 一文入门汽车毫米波雷达基本原理 :https://mp.weixin.qq.com/s/_EN7A5lKcz2Eh8dLnjE19w 毫米波雷达基础…...
深入理解 React 样式方案
React 的样式方案较多,在应用开发初期,开发者需要根据项目业务具体情况选择对应样式方案。React 样式方案主要有: 1. 内联样式 2. module css 3. css in js 4. tailwind css 这些方案中,均有各自的优势和缺点。 1. 方案优劣势 1. 内联样式: 简单直观,适合动态样式和…...
ABAP设计模式之---“Tell, Don’t Ask原则”
“Tell, Don’t Ask”是一种重要的面向对象编程设计原则,它强调的是对象之间如何有效地交流和协作。 1. 什么是 Tell, Don’t Ask 原则? 这个原则的核心思想是: “告诉一个对象该做什么,而不是询问一个对象的状态再对它作出决策。…...
Spring是如何实现无代理对象的循环依赖
无代理对象的循环依赖 什么是循环依赖解决方案实现方式测试验证 引入代理对象的影响创建代理对象问题分析 源码见:mini-spring 什么是循环依赖 循环依赖是指在对象创建过程中,两个或多个对象相互依赖,导致创建过程陷入死循环。以下通过一个简…...
多模态学习路线(2)——DL基础系列
目录 前言 一、归一化 1. Layer Normalization (LN) 2. Batch Normalization (BN) 3. Instance Normalization (IN) 4. Group Normalization (GN) 5. Root Mean Square Normalization(RMSNorm) 二、激活函数 1. Sigmoid激活函数(二分类&…...
