设计模式- 组合模式(Composite)结构|原理|优缺点|场景|示例

                                    设计模式(分类)        设计模式(六大原则)   

    创建型(5种)        工厂方法         抽象工厂模式        单例模式        建造者模式        原型模式

    结构型(7种)        适配器模式        装饰器模式        代理模式        ​​​​​​外观模式      桥接模式        组合模式       享元模式

    行为型(11种)       策略模式        模板方法模式        观察者模式        迭代器模式        责任链模式        命令模式

                                   备忘录模式          状态模式          访问者模式        中介者模式    


组合模式(Composite Pattern)是一种结构型设计模式,它允许你将对象组合成树形结构来表示“部分-整体”的层次结构。这种模式使得客户端可以以一致的方式处理单个对象(叶子节点)和组合对象(容器节点),无需关心处理的是个体还是群体。组合模式使得你可以将对象看作是树形结构中的节点,节点可以是简单的对象,也可以是包含其他节点的复合对象,这样就能形成一个层次化的结构。

模式结构

组合模式主要涉及以下几个角色:

  1. Component(抽象组件)

    • 定义了所有对象(包括叶子节点和容器节点)共享的公共接口。这个接口规定了如何访问和管理对象的子部件。
    • 通常会提供一个方法来添加、删除子组件,以及遍历子组件的方法。
  2. Leaf(叶子组件)

    • 是组合结构的终端节点,不包含任何子组件。
    • 实现Component接口,但对于那些与子组件管理无关的方法(如添加、删除子组件),可以提供空实现或者抛出异常。
  3. Composite(容器组件)

    • 包含一个或多个子组件,每个子组件也是Component的实例。
    • 实现Component接口,提供与管理子组件相关的方法的实际逻辑,如添加、删除子组件以及遍历子组件。
    • 可能会提供一些额外的方法来管理子组件集合,但这些方法通常不暴露给客户端。

工作原理

  • 客户端:通过Component接口与系统交互,无需区分处理的是叶子节点还是容器节点。
  • Component:定义了通用接口,为所有组件(包括叶子和容器)提供一致性。
  • Leaf:实现Component接口,但不包含子组件,因此与子组件管理相关的操作为空或无效。
  • Composite:除了实现Component接口外,还持有子组件的集合,并提供操作子组件的方法。当客户端请求操作时,Composite会递归地将请求传递给它的子组件。

优缺点

优点
  • 单一职责原则:组合模式使得叶子节点和容器节点都遵循单一职责原则,各自专注于自己的功能。
  • 透明性:客户端可以一致地处理单个对象和组合对象,无需知道处理的是叶子还是容器,提高了代码的透明性和简洁性。
  • 易于扩展:新类型的组件只需继承Component或实现相关接口即可加入到组合结构中,不影响已有代码。
缺点
  • 设计复杂度增加:为了实现组合模式,需要设计额外的抽象层和接口,使得系统变得相对复杂。
  • 递归操作可能导致性能问题:如果组合结构非常深,递归操作可能会导致栈溢出或效率下降。

适用场景

  • 系统需要处理对象的“部分-整体”关系:当需要表示对象的层级结构时,组合模式可以很好地表示这种关系。
  • 希望客户端以一致的方式处理单个对象和组合对象:组合模式使得客户端无需关心处理对象的具体类型,简化了客户端代码。
  • 希望简化新组件类型的添加:新的叶子节点或容器节点只需要符合Component接口即可轻松融入现有系统。

代码示例(以Java为例)

// 抽象组件
interface Component {
    void add(Component component);
    void remove(Component component);
    void operation();
}

// 叶子节点
class Leaf implements Component {
    private String name;

    public Leaf(String name) {
        this.name = name;
    }

    @Override
    public void add(Component component) {
        throw new UnsupportedOperationException("Leaves cannot have children.");
    }

    @Override
    public void remove(Component component) {
        throw new UnsupportedOperationException("Leaves cannot have children.");
    }

    @Override
    public void operation() {
        System.out.println("Leaf " + name + " performing operation.");
    }
}

// 容器节点
class Composite implements Component {
    private List<Component> children = new ArrayList<>();
    private String name;

    public Composite(String name) {
        this.name = name;
    }

    @Override
    public void add(Component component) {
        children.add(component);
    }

    @Override
    public void remove(Component component) {
        children.remove(component);
    }

    @Override
    public void operation() {
        System.out.println("Composite " + name + " performing operation.");
        for (Component child : children) {
            child.operation();
        }
    }
}

// 客户端代码
public class CompositePatternDemo {
    public static void main(String[] args) {
        Component root = new Composite("Root");
        root.add(new Leaf("Leaf A"));
        root.add(new Leaf("Leaf B"));

        Component branch = new Composite("Branch");
        branch.add(new Leaf("Leaf C"));
        branch.add(new Leaf("Leaf D"));

        root.add(branch);

        root.operation();
    }
}

在这个Java示例中:

  • Component接口定义了所有组件(叶子和容器)的通用接口,包括添加、删除子组件和执行操作的方法。
  • Leaf类实现了Component接口,但其addremove方法抛出异常,表示叶子节点无法添加或删除子节点。operation方法输出叶子节点执行操作的信息。
  • Composite类同样实现了Component接口,并维护了一个List<Component>来存储子组件。addremove方法实现了对子组件的增删操作。operation方法不仅执行自身操作,还递归地调用其子组件的operation方法。
  • 客户端代码创建了一个树状结构,并通过调用根节点的operation方法,以一致的方式处理整个组合结构中的所有组件。

 代码示例(以Python为例)

from abc import ABC, abstractmethod


class FileSystemObject(ABC):
    """抽象组件(Component)"""
    def __init__(self, name):
        self.name = name

    @abstractmethod
    def add(self, child):
        pass

    @abstractmethod
    def remove(self, child):
        pass

    @abstractmethod
    def get_child(self, index):
        pass

    @abstractmethod
    def operation(self):
        pass


class File(FileSystemObject):
    """叶子组件(Leaf)"""
    def add(self, child):
        raise TypeError("Cannot add children to a file")

    def remove(self, child):
        raise TypeError("Cannot remove children from a file")

    def get_child(self, index):
        raise IndexError("Files do not have children")

    def operation(self):
        return f"Performing operation on file: {self.name}"


class Directory(FileSystemObject):
    """复合组件(Composite)"""
    def __init__(self, name):
        super().__init__(name)
        self.children = []

    def add(self, child):
        self.children.append(child)

    def remove(self, child):
        self.children.remove(child)

    def get_child(self, index):
        return self.children[index]

    def operation(self):
        result = f"Performing operation on directory: {self.name}\n"
        for child in self.children:
            result += child.operation() + "\n"
        return result


# 客户端代码
if __name__ == "__main__":
    root_dir = Directory("root")
    dir_a = Directory("dir_a")
    dir_b = Directory("dir_b")
    file_1 = File("file_1.txt")
    file_2 = File("file_2.txt")

    root_dir.add(dir_a)
    root_dir.add(dir_b)
    dir_a.add(file_1)
    dir_b.add(file_2)

    print(root_dir.operation())

 在这个Python示例中:

  • FileSystemObject是抽象组件,使用abc模块中的ABC类和abstractmethod装饰器定义了所有文件系统对象共有的接口,如添加、删除子对象和执行操作等。
  • File类作为叶子组件,继承自FileSystemObject,实现了operation方法,并且其addremoveget_child方法抛出异常,表示文件不能包含子对象。
  • Directory类作为复合组件,同样继承自FileSystemObject,并且维护了一个children列表来存储子对象(可以是文件或子目录)。addremoveget_child方法实现了对子对象的管理。operation方法不仅执行自身操作,还递归地调用其子对象的operation方法,从而处理整个目录树。

客户端代码创建了根目录、子目录以及文件,并建立了它们之间的层级关系。最后,通过调用根目录的operation方法,以一致的方式处理整个文件系统的对象,无论是单个文件还是包含多个子对象的目录。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/570026.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

毫米波雷达模块用于海洋生态环境监测的技术方案研究

海洋生态环境是地球上重要的自然资源之一&#xff0c;对其进行监测和保护具有重要意义。毫米波雷达技术作为一种先进的感知技术&#xff0c;在海洋生态环境监测中具有广阔的应用前景。本文将探讨毫米波雷达模块用于海洋生态环境监测的技术方案&#xff0c;包括其原理、应用场景…

el-select下拉框远程搜索且多选时,编辑需要回显的一个简单案例

前端业务开发中不管使用vue2~3&#xff0c;还是react&#xff0c;angular各种前端技术栈&#xff0c;经常会遇到这种业务。一个下拉框Select中&#xff0c;不仅需要需要支持远程模糊搜索&#xff0c;还需要支持多选。并且在编辑时&#xff0c;还能正常把已经多选好的内容回显到…

redis主从复制,无法从redis读取最新的数据

目录 一、场景二、redis连接配置三、排查四、原因五、解决 一、场景 1、redis为主从复制模式 2、采用读写分离&#xff08;主节点写入&#xff0c;从节点读取&#xff09; 3、最新数据成功写入主节点&#xff0c;但从节点没有同步最新的数据 二、redis连接配置 #主节点 spr…

Linux——进程基本概念下篇

Linux——进程基本概念下篇 文章目录 Linux——进程基本概念下篇一、环境变量1.1 环境变量的定义1.2 环境变量的相关命令1.3 命令行参数1.4 本地变量和环境变量1.5 常规命令和内建命令 二、进程地址空间2.1 地址空间的概念2.2 页表和MMU2.3 地址空间的作用2.4 地址空间的好处 一…

Docker容器:docker基础

目录 一.docker前言 云计算服务模式 关于 docker 产品 虚拟化产品有哪些&#xff1f; ① 寄居架构 ② 源生架构 虚拟化技术概述 主流虚拟化产品对比 1. VMware系列 2. KVM/OpenStack 3. Xen 4. 其他半/全虚拟化产品 二. docker 的相关知识 1. docker 的概念 doc…

【古琴】倪诗韵古琴雷修系列(形制挺多的)

雷音系列雷修&#xff1a;“修”字取意善、美好的&#xff0c;更有“使之完美”之意。精品桐木或普通杉木制&#xff0c;栗壳色&#xff0c;纯鹿角霜生漆工艺。 方形龙池凤沼。红木配件&#xff0c;龙池上方有“倪诗韵”亲笔签名&#xff0c;凤沼下方&#xff0c;雁足上方居中位…

码头船只出行及配套货柜码放管理系统-毕设

毕业设计说明书 码头船只出行及配套货柜码放 管理系统 码头船只出行及配套货柜码放管理系统 摘要 伴随着全球化的发展&#xff0c;码头的物流和客运增多&#xff0c;码头业务迎来新的高峰。然而码头业务的增加&#xff0c;导致了人员成本和工作量的增多。为了解决这一基本问题&…

Bentley二次开发教程24-交互式类工具

交互式工具概念简述 本次内容主要涉及到交互式工具的使用&#xff0c;在MicroStation中&#xff0c;超过一半的功能都是以交互式工具的形式而存在的&#xff0c;因此交互式工具在MicroStation二次开发中便显得非常重要。当我们的鼠标或键盘在视图中产生交互操作时&#xff0c;…

各平台奇怪问题备忘录

微信小程序 小程序报错Page 页面路径 has not been register yet 描述&#xff1a;uniapp做微信小程序开发时&#xff0c;新增某页面后&#xff0c;小程序跳转该页面报错Page 页面路径 has not been register yet 已知&#xff1a;page.json已添加该页面&#xff0c;小程序a…

【Linux】文件目录及路径表示

1. Linux目录结构 在 Linux 系统中&#xff0c;有几个目录是比较重要的&#xff0c;平时需要注意不要误删除或者随意更改内部文件。 /etc&#xff1a; 这个是系统中的配置文件&#xff0c;如果更改了该目录下的某个文件可能会导致系统不能启动。 /bin, /sbin, /usr/bin, /usr…

vue快速入门(四十一)组件通信-依赖注入

注释很详细&#xff0c;直接上代码 上一篇 新增内容 祖先组件向下传值子代组件接受数据 源码 App.vue <template><div id"app"><sonComponent></sonComponent></div> </template> <script> import sonComponent from &qu…

python绘制随机地形地图

&#x1f47d;发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 当我们谈论计算机编程中的地图生成时&#xff0c;通常会想到游戏开发、仿真模拟或者数据可视…

vue3 修改路由中的meta属性

有些时候可能需要在路由跳转前后修改meta里面的相关属性值&#xff0c;这个时候就需要使用钩子函数&#xff08;路由守卫&#xff09;&#xff0c;钩子函数有全局钩子&#xff0c;局部组件钩子函数以及路由配置里面的钩子函数 &#xff08;这些也叫路由守卫&#xff09; 1.全局…

python数字验证码自动识别

&#x1f47d;发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 在网络上&#xff0c;许多网站和应用程序使用验证码&#xff08;Completely Automated Publ…

Ubuntu系统开机长

Ubuntu系统开机长 1. 检查开机自启动软件的所占时间2. 将耗时最高的禁止开机自启动 1. 检查开机自启动软件的所占时间 systemd-analyze blame2. 将耗时最高的禁止开机自启动 sudo systemctl disable networking.service这个耗时是有阈值的&#xff0c;一般大于15s的算&#x…

【数据结构与算法】8.二叉树的基本概念|前序遍历|中序遍历|后序遍历

&#x1f4da;博客主页&#xff1a;爱敲代码的小杨. ✨专栏&#xff1a;《Java SE语法》 | 《数据结构与算法》 | 《C生万物》 |《MySQL探索之旅》 |《Web世界探险家》 ❤️感谢大家点赞&#x1f44d;&#x1f3fb;收藏⭐评论✍&#x1f3fb;&#xff0c;您的三连就是我持续更…

vue封装请求、合并js、合并多个js

vue封装请求、合并js、合并多个js 作为一个后端开发&#xff0c;写前端时发现&#xff0c;每次导入api接口都会有一堆代码&#xff0c;像下面这样&#xff1a; import {footprintList, footprintDelete} from /api/userApi.js import {addressList} from /api/userApi.js impor…

设置Linux开发板开机自启动QT程序的报错解决办法

设置Linux开发板开机自启动QT程序报错解决办法 设置开发板开机自启动QT 打开 /etc/init.d/rsC 文件&#xff0c;添加以下内容 cd / ./my_start_run.shmy_start_run.sh 是自己编写的自启动脚本&#xff0c;内容例如下&#xff1a;(也可以将这些直接写到 /etc/init.d/rsC 文件…

【算法刷题 | 贪心算法02】4.24(摆动序列)

文章目录 3.摆动序列3.1题目3.2解法&#xff1a;贪心3.2.1贪心思路3.2.2代码实现 3.摆动序列 3.1题目 如果连续数字之间的差严格地在正数和负数之间交替&#xff0c;则数字序列称为 摆动序列 。 第一个差&#xff08;如果存在的话&#xff09;可能是正数或负数。仅有一个元素…

嵌入式总线协议基础教学

在嵌入式系统设计中&#xff0c;总线协议&#xff08;bus protocols&#xff09;扮演着至关重要的角色&#xff0c;它们定义了设备如何在共享通信路径上交换数据。 本文将介绍两种常见的嵌入式总线协议&#xff1a;IC&#xff08;Inter-Integrated Circuit&#xff09;和SPI&a…