设计模式——组合设计模式(结构型)

news2025/7/25 10:40:45

摘要

组合设计模式是一种结构型设计模式,用于将对象组合成树形结构以表示“部分-整体”的层次结构,使客户端对单个对象和组合对象具有一致的访问方式。它包含抽象组件、叶子节点和组合节点,具有统一处理、支持递归结构和易扩展等优点,适用树形结构场景,如组织架构、菜单、规则集等。

1. 组合设计模式定义

组合设计模式(Composite Pattern)是一种结构型设计模式,它用于将对象组合成树形结构以表示“部分-整体”的层次结构,使得客户端对单个对象和组合对象具有一致的访问方式。组合模式允许你将对象组合成树形结构来表示“整体/部分”层次结构。组合模式使得客户端对单个对象和组合对象的使用具有一致性

1.1.1. ✅ UML 结构图(简化):

         Component(抽象组件)
            ↑
     ┌──────┴──────┐
Leaf(叶子节点)   Composite(容器/组合节点)
  • Component:定义组合中对象的接口,可以是抽象类或接口。
  • Leaf:叶子节点,表示树结构中的子节点,不能再包含其他子对象。
  • Composite:组合节点,表示可以拥有子节点的对象,它实现了 Component 接口,并维护子组件集合。

1.1.2. ✅ 举例说明(风控场景类比):

比如在风控系统中,有一套规则系统:

  • 单一规则(如“手机号黑名单检查”)是 Leaf
  • 组合规则(如“黑名单检查 + 地域异常组合规则”)是 Composite
  • 客户端调用统一的 evaluate() 方法即可,不需要关心规则是单一还是组合。

1.1.3. ✅ 核心优势:

优点

说明

统一处理单个和组合对象

客户端代码一致,不用区别对待

支持递归结构

适用于树形结构,如组织架构、菜单、规则集

易于扩展

增加新的 LeafComposite 不影响原有代码

2. 组合设计模式结构

2.1. 组合设计模式类图

结构说明

  1. 组件 (Component) 接口描述了树中简单项目和复杂项目所共有的操作。
  2. 叶节点 (Leaf) 是树的基本结构, 它不包含子项目。一般情况下, 叶节点最终会完成大部分的实际工作, 因为它们无法将工作指派给其他部分。
  3. 容器 (Container)——又名 “组合 (Composite)”——是包含叶节点或其他容器等子项目的单位。 容器不知道其子项目所属的具体类, 它只通过通用的组件接口与其子项目交互。容器接收到请求后会将工作分配给自己的子项目, 处理中间结果, 然后将最终结果返回给客户端。
  4. 客户端 (Client) 通过组件接口与所有项目交互。 因此, 客户端能以相同方式与树状结构中的简单或复杂项目交互。

2.2. 组合模式时序图

3. 组合设计模式实现方式

将单个对象(叶子节点)和组合对象(容器节点)统一抽象为一个组件接口,客户端无需关心操作对象是单一元素还是组合结构,均通过统一接口访问。

3.1. Step 1:定义统一接口 Component

public interface Component {
    void operation();
}

3.2. Step 2:实现叶子节点 Leaf

public class Leaf implements Component {

    private String name;

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

    @Override
    public void operation() {
        System.out.println("Leaf [" + name + "] 执行操作");
    }
}

3.3. Step 3:实现组合节点 Composite

import java.util.ArrayList;
import java.util.List;

public class Composite implements Component {

    private String name;
    private List<Component> children = new ArrayList<>();

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

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

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

    @Override
    public void operation() {
        System.out.println("Composite [" + name + "] 开始操作");
        for (Component child : children) {
            child.operation();
        }
    }
}

3.4. Step 4:客户端使用(Client)

public class Client {
    public static void main(String[] args) {
        Leaf leaf1 = new Leaf("规则 A");
        Leaf leaf2 = new Leaf("规则 B");

        Composite composite1 = new Composite("组合规则1");
        composite1.add(leaf1);
        composite1.add(leaf2);

        Leaf leaf3 = new Leaf("规则 C");
        Composite root = new Composite("根规则");
        root.add(composite1);
        root.add(leaf3);

        root.operation(); // 统一调用
    }
}

4. 组合设计模式适合场景

组合设计模式(Composite Pattern)适用于 “树形结构” 的场景,它能让客户端以统一方式处理单个对象和组合对象。以下是其适合与不适合使用的场景总结:

4.1. ✅ 适合使用组合设计模式的场景

场景

说明

具有树形结构的业务模型

如文件系统、组织架构、菜单栏、风控规则树等。

需要统一处理单个对象和组合对象

客户端希望一致地使用所有元素(比如 evaluate()operation()),而不关心它们是叶子还是组合。

组合对象和单个对象行为一致

当组合对象与叶子节点有相同的行为逻辑(如日志打印、状态传递等)。

需要支持递归组合对象

组合中可以包含其他组合(嵌套结构),比如组合规则中嵌套组合规则。

客户端不应依赖具体实现结构

只与 Component 接口交互,提高可扩展性与解耦能力。

4.2. ❌ 不适合使用组合设计模式的场景

场景

原因

对象之间结构关系简单

如果业务不涉及树形结构,引入组合反而增加系统复杂度。

叶子节点与组合节点的行为差异很大

若行为差异明显(如操作参数、逻辑完全不同),统一接口会导致实现臃肿。

对性能要求极高的系统

递归遍历树结构可能导致性能瓶颈,不如精细控制流程。

对象的生命周期和依赖复杂

比如依赖注入、事务、缓存等特性在嵌套结构中处理较困难。

频繁变动的数据结构

若组合结构经常调整、扩展,维护成本会变高,灵活性不如策略模式或责任链模式。

4.3. 📝 实战建议(风控项目中的应用)

项目结构

是否适合组合模式

风控规则树(嵌套组合规则 + 原子规则)

✅ 适合

单一条件判断规则(如手机号是否存在)

❌ 不适合(可用策略/责任链)

配置驱动型规则执行器

✅ 适合(组合模式 + Spring 注入)

强依赖流程顺序的规则链

❌ 不适合(更适合责任链模式)

5. 组合设计模式实战示例

组合设计模式实战示例 — 风控规则引擎

5.1. 统一接口 Component

public interface RuleComponent {
    boolean evaluate(RiskContext context);
}
  • evaluate 方法表示对风控上下文做规则判断,返回是否通过。

5.2. 叶子节点实现(单一规则)

import org.springframework.stereotype.Component;
import javax.annotation.Resource;

@Component("blacklistRule")
public class BlacklistRule implements RuleComponent {

    @Resource(name = "blacklistService")
    private BlacklistService blacklistService; // 依赖注入,模拟黑名单校验服务

    @Override
    public boolean evaluate(RiskContext context) {
        System.out.println("执行黑名单规则");
        return !blacklistService.isBlacklisted(context.getUserId());
    }
}

5.3. 组合节点实现(组合规则)

import org.springframework.stereotype.Component;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.ArrayList;
import java.util.List;

@Component("compositeRule")
public class CompositeRule implements RuleComponent {

    @Autowired
    private List<RuleComponent> children;  // 注入所有 RuleComponent 实现类(包括叶子和组合)

    @Override
    public boolean evaluate(RiskContext context) {
        System.out.println("执行组合规则,包含 " + children.size() + " 条子规则");
        for (RuleComponent rule : children) {
            if (!rule.evaluate(context)) {
                return false;  // 只要有一条规则不通过,整个组合规则失败
            }
        }
        return true;
    }
}

注意: 这里 children 注入的是所有 RuleComponent 实现,实际场景中可能用 qualifier 或配置区分组合里的具体规则。

5.4. 风控上下文类

public class RiskContext {
    private String userId;
    private double loanAmount;
    // 其他业务相关参数

    // 省略构造、getter、setter
    public RiskContext(String userId, double loanAmount) {
        this.userId = userId;
        this.loanAmount = loanAmount;
    }
    public String getUserId() { return userId; }
    public double getLoanAmount() { return loanAmount; }
}

5.5. 依赖服务示例(模拟黑名单服务)

import org.springframework.stereotype.Service;

@Service("blacklistService")
public class BlacklistService {
    public boolean isBlacklisted(String userId) {
        // 模拟黑名单查询,假设 userId 为 "1001" 是黑名单
        return "1001".equals(userId);
    }
}

5.6. 客户端调用(风控引擎)

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class RiskEngine {

    @Autowired
    private RuleComponent compositeRule;  // 注入组合规则,入口

    public boolean evaluate(RiskContext context) {
        return compositeRule.evaluate(context);
    }
}

5.7. SpringBoot 启动类及测试

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.beans.factory.annotation.Autowired;

@SpringBootApplication
public class RiskApp implements CommandLineRunner {

    @Autowired
    private RiskEngine riskEngine;

    public static void main(String[] args) {
        SpringApplication.run(RiskApp.class, args);
    }

    @Override
    public void run(String... args) {
        RiskContext context1 = new RiskContext("1000", 5000);
        System.out.println("用户1000风控结果: " + riskEngine.evaluate(context1));

        RiskContext context2 = new RiskContext("1001", 5000);
        System.out.println("用户1001风控结果: " + riskEngine.evaluate(context2));
    }
}

总结

  • 统一接口 RuleComponent,定义规则通用方法。
  • 叶子节点实现单个规则(如黑名单),组合节点实现多个规则的组合逻辑。
  • 通过 @Autowired 注入,Spring 自动管理组件实例。
  • 业务调用时直接使用组合规则,自动递归调用子规则。
  • 采用字段注入方式,符合你“不用构造函数注入”的要求。

6. 组合设计模式思考

博文参考

  • 开放平台
  • 组合设计模式

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

EMO2:基于末端执行器引导的音频驱动虚拟形象视频生成

今天带来EMO2&#xff08;全称End-Effector Guided Audio-Driven Avatar Video Generation&#xff09;是阿里巴巴智能计算研究院研发的创新型音频驱动视频生成技术。该技术通过结合音频输入和静态人像照片&#xff0c;生成高度逼真且富有表现力的动态视频内容&#xff0c;值得…

Python打卡训练营Day43

DAY 43 复习日 作业&#xff1a; kaggle找到一个图像数据集&#xff0c;用cnn网络进行训练并且用grad-cam做可视化 数据集地址&#xff1a;Lung Nodule Malignancy 肺结核良恶性判断 进阶&#xff1a;并拆分成多个文件 import os import pandas as pd import numpy as np from…

PHP7+MySQL5.6 查立得轻量级公交查询系统

# PHP7MySQL5.6 查立得轻量级公交查询系统 ## 系统简介 本系统是一个基于PHP7和MySQL5.6的轻量级公交查询系统(40KB级)&#xff0c;支持线路查询、站点查询和换乘查询功能。系统采用原生PHPMySQL开发&#xff0c;无需第三方框架&#xff0c;适合手机端访问。 首发版本&#x…

如何做好一个决策:基于 Excel的决策树+敏感性分析应用(针对多个变量)

本文是对《如何做好一个决策:基于 Excel的决策树+敏感性分析应用》一文的补充。 示例背景 决策问题:是否开发新产品? 关键变量: 开发成本(B2):$500K, $700K, $1M高需求概率(B4):30%, 50%, 70%高需求收入(C4

Azure DevOps 管道部署系列之一本地服务器

Azure DevOps 是一个帮助改进 SDLC(软件开发生命周期)的平台。 在本文中,我们将使用 Azure Pipelines 创建自动化部署。 Azure DevOps 团队将 Azure Pipelines 定义为“使用 CI/CD 构建、测试和部署,适用于任何语言、平台和云平台”。 在这里,我将解释如何在 Azure Dev…

Celery简介

一、什么是异步任务队列 异步任务队列是指一种用于管理和调度异步执行任务的机制。具体来说&#xff0c;它允许将任务放入队列中&#xff0c;然后由后台进程异步处理这些任务&#xff0c;而不会阻塞主线程的执行。这种设计使得系统能够高效地处理耗时操作&#xff0c;同时保持…

基于 GitLab CI + Inno Setup 实现 Windows 程序自动化打包发布方案

在 Windows 桌面应用开发中&#xff0c;实现自动化构建与打包发布是一项非常实用的工程实践。本文以我在开发PackTes项目时的为例&#xff0c;介绍如何通过 GitLab CI 配合 Inno Setup、批处理脚本、Qt 构建工具&#xff0c;实现版本化打包并发布到共享目录的完整流程。 项目地…

web架构2------(nginx多站点配置,include配置文件,日志,basic认证,ssl认证)

一.前言 前面我们介绍了一下nginx的安装和基础配置&#xff0c;今天继续来深入讲解一下nginx的其他配置 二.nginx多站点配置 一个nginx上可以运行多个网站。有多种方式&#xff1a; http:// ip/域名 端口 URI 其中&#xff0c;ip/域名变了&#xff0c;那么网站入口就变了…

AI 的早期萌芽?用 Swift 演绎约翰·康威的「生命游戏」

文章目录 摘要描述题解答案题解代码分析示例测试及结果时间复杂度空间复杂度总结 摘要 你有没有想过&#xff0c;能不能通过简单的规则模拟出生与死亡&#xff1f;「生命游戏」正是这样一种充满魅力的数学模拟系统。这篇文章我们来聊聊它的规则到底有多神奇&#xff0c;并用 S…

go|channel源码分析

文章目录 channelhchanmakechanchansendchanrecvcomplieclosechan channel 先看一下源码中的说明 At least one of c.sendq and c.recvq is empty, except for the case of an unbuffered channel with a single goroutine blocked on it for both sending and receiving usin…

【大模型学习】项目练习:视频文本生成器

&#x1f680;实现视频脚本生成器 视频文本生成器 &#x1f4da;目录 一、游戏设计思路二、完整代码解析三、扩展方向建议四、想说的话 一、⛳设计思路 本视频脚本生成器采用模块化设计&#xff0c;主要包含三大核心模块&#xff1a; 显示模块&#xff1a;处理用户输入和…

【Rust】Rust获取命令行参数以及IO操作

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

【Redis】Zset 有序集合

文章目录 常用命令zaddzcardzcountzrange && zrevrangezrangebyscorezpopmax && bzpopmaxzpopmin && zpopmaxzrank && zrevrankzscorezremzremrangebyrankzremrangebyscorezincrby 集合间操作交集 zinterstore并集 zunionstore 内部编码应用场…

manus对比ChatGPT-Deep reaserch进行研究类论文数据分析!谁更胜一筹?

目录 没有账号&#xff0c;只能挑选一个案例 1、manus的效果 Step-1&#xff1a;直接看结果 Step-2&#xff1a;看看其他文件的细节 Step-3&#xff1a;看最终报告 2、Deep reaserch 3、Deep reaserch进行行业分析 总结一下&#xff1a; 大家好这里是学术Anan&#xff…

【 HarmonyOS 5 入门系列 】鸿蒙HarmonyOS示例项目讲解

【 HarmonyOS 5 入门系列 】鸿蒙HarmonyOS示例项目讲解 一、前言&#xff1a;移动开发声明式 UI 框架的技术变革 在移动操作系统的发展历程中&#xff0c;UI 开发模式经历了从命令式到声明式的重大变革。 根据华为开发者联盟 2024 年数据报告显示&#xff0c;HarmonyOS 设备…

用提示词写程序(3),VSCODE+Claude3.5+deepseek开发edge扩展插件V2

edge扩展插件;筛选书签,跳转搜索,设置背景 链接: https://pan.baidu.com/s/1nfnwQXCkePRnRh5ltFyfag?pwd86se 提取码: 86se 导入解压的扩展文件夹: 导入扩展成功: edge扩展插件;筛选书签,跳转搜索,设置背景

初识PS(Photoshop)

初识PS&#xff08;Photoshop&#xff09; 1、Photoshop界面 2、常用快捷键

go语言的GMP(基础)

1.概念梳理 1.1线程 通常语义中的线程&#xff0c;指的是内核级线程&#xff0c;核心点如下&#xff1a; &#xff08;1&#xff09;是操作系统最小调度单元&#xff1b; &#xff08;2&#xff09;创建、销毁、调度交由内核完成&#xff0c;cpu 需完成用户态与内核态间的切…

电路图识图基础知识-高、低压供配电系统电气系统的继电自动装置(十三)

电气系统的继电自动装置 在供电系统中为保证系统的可靠性&#xff0c;保证重要负荷的不间断供电&#xff0c;常采用自动重合闸装置和备用电源自动投入装置。 1 自动重合闸装置 供配电系统多年运行实践表明&#xff0c;架空线路发生的故障多属于暂时性故障&#xff0c;如雷击…

Qt实现的水波进度条和温度进度条

一.效果 二.原理 1.水波 要模拟波浪,就要首先画出一条波浪线,正弦余弦曲线就很适合。 y=A*sin(ω*x+φ)+k y=A*cos(ω*x+φ)+k 这是正弦余弦曲线的公式,要想实现水波效果,那需要两条曲线,一条曲线的波峰对着另外一条曲线的波谷,要实现这样的曲线效果,只有让正弦曲线前移…