【再谈设计模式】组合模式~层次构建的多面手

ops/2024/12/14 11:40:50/

一、引言

        在软件开发的世界里,我们经常面临着处理对象之间复杂关系的挑战。如何有效地表示对象的部分 - 整体层次结构,并且能够以一种统一的方式操作这些对象,是一个值得探讨的问题。组合模式(Composite Pattern)为我们提供了一种优雅的解决方案,它使得客户端可以像处理单个对象一样处理对象组合。这种模式在很多领域都有着广泛的应用,从图形界面的构建到文件系统的组织,都能看到它的身影。

二、定义与描述

        组合模式是一种结构型设计模式,它允许将对象组合成树形结构来表示“部分 - 整体”的层次关系。组合模式使得用户对单个对象和组合对象的使用具有一致性。在组合模式中,有两种基本类型的对象:叶节点(Leaf)和组合节点(Composite)。叶节点是没有子节点的对象,而组合节点可以包含叶节点和其他组合节点。

三、抽象背景

        在许多实际的软件系统中,我们需要处理具有层次结构的数据或对象。例如,在文件系统中,文件夹可以包含文件和其他文件夹;在图形用户界面中,一个容器组件(如面板)可以包含其他组件(如按钮、文本框等)。如果没有一种合适的设计模式,对这种层次结构的操作将会变得复杂和难以维护。例如,我们可能需要编写大量的条件判断语句来区分对象是单个的还是组合的,这会导致代码的复杂性增加,并且容易出错。

四、适用场景与现实问题解决

  • 层次结构的数据存储和操作
    • 例如,在一个企业组织结构管理系统中,公司由多个部门组成,部门又可以包含子部门和员工。使用组合模式,可以方便地对整个组织结构进行管理,如统计员工数量、计算部门预算等。

  • 图形界面组件管理
    • 在图形界面开发中,窗口、面板、按钮等组件构成了一个层次结构。组合模式允许我们以统一的方式处理这些组件,例如,对整个界面进行布局调整或者显示/隐藏操作。

五、组合模式的现实生活的例子

  • 文件系统
    • 文件夹可以看作是组合节点,文件则是叶节点。我们可以对文件夹进行操作,如删除文件夹(这会递归地删除文件夹中的所有文件和子文件夹),也可以对单个文件进行操作,如打开、重命名等。

  • 菜单结构
    • 在餐厅的菜单中,菜单可以包含子菜单和菜品。整个菜单是一个组合结构,我们可以对整个菜单进行显示、定价等操作,也可以对单个菜品进行操作,如调整价格、描述等。

六、初衷与问题解决

        初衷是为了简化对具有层次结构的对象的操作,使得客户端不需要区分对象是单个的还是组合的。通过将对象组织成树形结构,并定义统一的操作接口,解决了在处理层次结构时代码复杂、难以维护的问题。

七、代码示例

实现类图举例:

  • 首先定义了抽象类 Component,它有一个私有属性 name,一个构造函数和一个抽象方法 operation
  • 然后定义了 Leaf 类,它继承自 Component 类,有自己的构造函数和实现了 operation 方法。
  • 接着定义了 Composite 类,它也继承自 Component 类,有一个私有属性 children 用于存储子组件,有构造函数、添加和移除子组件的方法以及实现了 operation 方法。
  • 最后通过关系符号表示了 Leaf 和 Composite 与 Component 的继承关系。

使用时序图:

  • Client 作为参与者,首先创建了 root(组合对象)、多个叶节点(leaf1leaf2subLeaf1subLeaf2)和一个子组合节点(subComposite)。
  • 然后将子叶节点添加到子组合节点中,将叶节点和子组合节点添加到根组合节点 root 中。
  • 最后,Client 调用 root 的 operation 方法,按照组合模式的逻辑,这个操作会递归地调用其包含的所有子对象(叶节点和子组合节点)的 operation 方法。

Java

// 组件抽象类
abstract class Component {protected String name;public Component(String name) {this.name = name;}public abstract void operation();
}// 叶节点类
class Leaf extends Component {public Leaf(String name) {super(name);}@Overridepublic void operation() {System.out.println("叶节点 " + name + " 执行操作");}
}// 组合节点类
class Composite extends Component {private java.util.ArrayList<Component> children = new java.util.ArrayList<>();public Composite(String name) {super(name);}public void add(Component component) {children.add(component);}public void remove(Component component) {children.remove(component);}@Overridepublic void operation() {System.out.println("组合节点 " + name + " 执行操作");for (Component child : children) {child.operation();}}
}public class Main {public static void main(String[] args) {Composite root = new Composite("根节点");Leaf leaf1 = new Leaf("叶节点1");Leaf leaf2 = new Leaf("叶节点2");Composite subComposite = new Composite("子组合节点");Leaf subLeaf1 = new Leaf("子叶节点1");Leaf subLeaf2 = new Leaf("子叶节点2");subComposite.add(subLeaf1);subComposite.add(subLeaf2);root.add(leaf1);root.add(leaf2);root.add(subComposite);root.operation();}
}

C++

#include <iostream>
#include <vector>// 组件抽象类
class Component {
protected:std::string name;
public:Component(std::string name) : name(name) {}virtual void operation() = 0;
};// 叶节点类
class Leaf : public Component {
public:Leaf(std::string name) : Component(name) {}void operation() override {std::cout << "叶节点 " << name << " 执行操作" << std::endl;}
};// 组合节点类
class Composite : public Component {
private:std::vector<Component*> children;
public:Composite(std::string name) : Component(name) {}void add(Component* component) {children.push_back(component);}void remove(Component* component) {for (auto it = children.begin(); it!= children.end(); ++it) {if (*it == component) {children.erase(it);break;}}}void operation() override {std::cout << "组合节点 " << name << " 执行操作" << std::endl;for (auto child : children) {child->operation();}}
};int main() {Composite root("根节点");Leaf leaf1("叶节点1");Leaf leaf2("叶节点2");Composite subComposite("子组合节点");Leaf subLeaf1("子叶节点1");Leaf subLeaf2("子叶节点2");subComposite.add(&subLeaf1);subComposite.add(&subLeaf2);root.add(&leaf1);root.add(&leaf2);root.add(&subComposite);root.operation();return 0;
}

Python

# 组件抽象类
class Component:def __init__(self, name):self.name = namedef operation(self):pass# 叶节点类
class Leaf(Component):def operation(self):print(f"叶节点 {self.name} 执行操作")# 组合节点类
class Composite(Component):def __init__(self, name):super().__init__(name)self.children = []def add(self, component):self.children.append(component)def remove(self, component):self.children.remove(component)def operation(self):print(f"组合节点 {self.name} 执行操作")for child in self.children:child.operation()if __name__ == "__main__":root = Composite("根节点")leaf1 = Leaf("叶节点1")leaf2 = Leaf("叶节点2")subComposite = Composite("子组合节点")subLeaf1 = Leaf("子叶节点1")subLeaf2 = Leaf("子叶节点2")subComposite.add(subLeaf1)subComposite.add(subLeaf2)root.add(leaf1)root.add(leaf2)root.add(subComposite)root.operation()

Go

package mainimport ("fmt"
)// 组件接口
type Component interface {operation()
}// 叶节点结构体
type Leaf struct {name string
}func (l *Leaf) operation() {fmt.Printf("叶节点 %s 执行操作\n", l.name)
}// 组合节点结构体
type Composite struct {name     stringchildren []Component
}func (c *Composite) add(component Component) {c.children = append(c.children, component)
}func (c *Composite) remove(component Component) {for i, child := range c.children {if child == component {c.children = append(c.children[:i], c.children[i+1:]...)break}}
}func (c *Composite) operation() {fmt.Printf("组合节点 %s 执行操作\n", c.name)for _, child := range c.children {child.operation()}
}func main() {root := &Composite{"根节点", []Component{}}leaf1 := &Leaf{"叶节点1"}leaf2 := &Leaf{"叶节点2"}subComposite := &Composite{"子组合节点", []Component{}}subLeaf1 := &Leaf{"子叶节点1"}subLeaf2 := &Leaf{"子叶节点2"}subComposite.add(subLeaf1)subComposite.add(subLeaf2)root.add(leaf1)root.add(leaf2)root.add(subComposite)root.operation()
}

八、组合模式的优缺点

优点

  • 简化客户端代码
    • 客户端不需要区分操作的对象是单个的还是组合的,统一的接口使得操作更加简单。
  • 可扩展性好
    • 容易添加新的叶节点或组合节点类型,对现有代码的影响较小。
  • 层次结构清晰
    • 能够清晰地表示对象之间的层次关系,便于理解和维护。

缺点

  • 限制叶节点和组合节点的共性
    • 在定义抽象组件时,需要考虑叶节点和组合节点的共性操作,如果共性操作较少,可能会导致抽象组件接口的定义不够合理。
  • 可能导致设计过度复杂
    • 在一些简单的层次结构场景中,如果使用组合模式,可能会引入不必要的复杂性。

九、组合模式的升级版

  • 安全组合模式
    • 在普通的组合模式中,叶节点和组合节点都实现相同的接口,这可能会导致一些安全隐患,例如叶节点可能被误当作组合节点进行添加操作。安全组合模式通过将叶节点和组合节点的接口分开定义,增加了类型安全性。

  • 透明组合模式
    • 透明组合模式中,叶节点和组合节点具有完全相同的接口,这使得客户端可以完全透明地操作对象。但是这种模式可能会导致一些语义上的混淆,例如叶节点可能被赋予了一些对它无意义的操作(如添加子节点)。在实际应用中,可以根据具体需求选择合适的组合模式升级版本。


http://www.ppmy.cn/ops/141804.html

相关文章

《蓝桥杯比赛规划》

大家好啊&#xff01;我是NiJiMingCheng 我的博客&#xff1a;NiJiMingCheng 这节课我们来分享蓝桥杯比赛规划&#xff0c;好的规划会给我们的学习带来良好的收益&#xff0c;废话少说接下来就让我们进入学习规划吧&#xff0c;加油哦&#xff01;&#xff01;&#xff01; 一、…

虚幻引擎Actor类生命周期

AActor构造函数 在AActor类的构造函数中,虚幻引擎会初始化与该Actor相关的一些关键属性,比如: 默认的组件(如RootComponent、MeshComponent等)。默认的属性设置,例如位置、旋转、缩放等。还会调用BeginPlay等生命周期函数,但在构造函数中,这些函数不会执行。当你在场景…

SpringBoot + MyBatis 实现号段模式的分布式ID

号段模式是一种常见的 ID 生成策略&#xff0c;在高并发场景中广泛应用。其核心思想是&#xff0c;发号服务每次从数据库获取一批 ID&#xff0c;并将这些 ID 缓存到本地。业务系统每次请求 ID 时&#xff0c;首先会判断本地缓存是否有可用的 ID。如果有&#xff0c;则直接分配…

Not using native diff for overlay2, this may cause degraded performance……

问题现象 案例&#xff1a;Anolis 8.9&#xff08;4.19.91-26.an8.x86_64&#xff09; Overlay2存储驱动程序&#xff09; 当我们安装好Docker之后&#xff0c;通过systemctl status docker -l 会发现有一个告警信息&#xff1a;levelwarning msg"Not using native dif…

Maven、mybatis框架

一、Maven介绍 1.概念&#xff1a; Maven项目对象模型(POM)&#xff0c;可以通过一小段描述信息来管理项目的构建&#xff0c;报告和文档的项目管理工具软件。 2.为啥使用maven: 之前项目中需要引入大量的jar包。这些jar从网上下载&#xff0c;可能下载地址不同意。这些jar之间…

AI开源南京分享会回顾录

AI 开源南京分享会&#xff0c;已于2024年11月30日下午在国浩律师&#xff08;南京&#xff09;事务所5楼会议厅成功举办。此次活动由 KCC南京、PowerData、RISC-Verse 联合主办&#xff0c;国浩律师&#xff08;南京&#xff09;事务所协办。 活动以“开源视角的 AI 对话”为主…

TcpServer 服务器优化之后,加了多线程,对心跳包进行优化

TcpServer 服务器优化之后&#xff0c;加了多线程&#xff0c;对心跳包进行优化 TcpServer.h #ifndef TCPSERVER_H #define TCPSERVER_H#include <iostream> #include <winsock2.h> #include <ws2tcpip.h> #include <vector> #include <map> #…

C语言(指针基础练习)

删除数组中的元素 数组的元素在内存地址中是连续的&#xff0c;不能单独删除数组中的某个元素&#xff0c;只能覆盖。 #include <stdio.h> #include <stdbool.h>// 函数声明 int deleteElement(int arr[], int size, int element);int main() {int arr[] {1, 2, 3…