架构技能(六):软件设计(下)

我们知道,软件设计包括软件的整体架构设计和模块的详细设计。

在上一篇文章(见 《架构技能(五):软件设计(上)》)谈了软件的整体架构设计,今天聊一下模块的详细设计。

模块的详细设计,重点体现在需要设计几个具有明确职责的角色,以及角色之间应该设计成什么样的关系,这里的角色一般是一个很大的类;然后对每一个角色继续分析需要设计几个类来实现角色职责,以及类与类之间应该设计成什么样的关系。

模块的详细设计,应该像架构设计一样,由高到低,逐层进行。

在之前的文章(见《架构技能(一):软件架构》)中,分析过软件架构,见下图。

软件架构是指软件系统的顶层结构,包含具有明确职责的角色,这些角色通过相互协作使软件系统提供业务能力。

一个模块应该设计成几个类,取决于业务场景;但是类与类之间的关系是固定的模式,从原子化不可再分的角度看,共包括六类关系:依赖、关联、聚合、组合、继承、实现;“继承” 和 “实现” 更多是为了多态特性的实现需要,在基础框架中应用普遍,在业务系统中以前四种类关系应用为主。

依赖

依赖方与被依赖方是 “使用” 关系,从代码上看,被依赖方对象往往出现在依赖方对象的方法内部。比如:依赖方是 Person,被依赖方是 Bus,人 “使用” 公共汽车,见下图。

关联

关联方与被关联方是 “拥有” 关系,从代码上看,被关联方对象往往出现在关联方对象的成员变量上。比如:关联方是 Person,被关联方是 Phone,人 “拥有” 手机,见下图。

聚合

聚合方与被聚合方是整体与个体之间的关联关系,整体 “拥有” 个体,个体 “聚合” 成整体。从代码上看,被聚合方对象出现在关联方对象的成员变量上。比如:聚合方是 Company,被聚合方是 Staff,公司 “拥有” 员工,员工 “聚合” 成公司,见下图。

组合

组合方与被组合方仍是整体与个体之间的关联关系,整体 “拥有” 个体,个体 “组合” 成整体,与聚合关系不同的是,被组合方不能独立存在。从代码上看,被组合方对象出现在组合对象的成员变量上,且在组合对象内部进行初始化。比如:组合方是 Computer,被组合方是 Cpu,计算机 “拥有” CPU,CPU “组合” 成了计算机,且 CPU 离开计算机不能独立存在,见下图。

总结一下在软件模块详细设计中,类与类之间最常用的四种关系:

  • 依赖的核心是 “使用”,两个对象在同一层次上;

  • 关联的核心是 “拥有”,两个对象在同一层次上;

  • 聚合的核心是 整体与个体之间的 “拥有” 与 “聚合”,个体离开整体后对象的生命周期可以继续;

  • 组合的核心是 整体与个体之间的 “拥有” 与 “组合”,个体离开整体后对象的生命周期结束。

理解这四类对象关系,并烂熟于心,那就可以对软件业务系统模块进行详细设计了。

这里,我们以 基于时间轮的 HTTP 长轮询获取消息为例,描述软件模块详细设计的过程。

先简述一下 HTTP 长轮询,见下图。

  • http 客户端向 http 服务端发起 http 请求;

  • http 服务端 hold 住该请求,不会立刻返回 http 响应;

  • http 服务端只有满足两个条件中的任何一个才会返回 http 响应: 要么超过一定时间(超时),要么产生了属于客户端的消息

  • http 客户端收到响应后,再次发起 http 请求,重复上述过程。

在这个场景里, http 服务端应该如何设计呢?

分析上述时序图,可以很容易得出:http 服务端需要包含三个角色,即 http服务、消息模块和时间轮模块,三个角色之间的关系,见下图。

  • http 客户端向 http 服务端发起 http 请求;

  • http 服务端 hold 住该请求,不会立刻返回 http 响应;

  • http 服务端只有满足两个条件中的任何一个才会返回 http 响应: 要么超过一定时间(超时),要么产生了属于客户端的消息

  • http 客户端收到响应后,再次发起 http 请求,重复上述过程。

在这个场景里, http 服务端应该如何设计呢?

分析上述时序图,可以很容易得出:http 服务端需要包含三个角色,即 http服务、消息模块和时间轮模块,三个角色之间的关系,见下图。

  • 时间轮对于 http 服务来说是一个工具,http 服务使用时间轮这个工具进行管道的注册,所以 HttpService 类 “依赖” 于 Wheel 类;

  • 消息模块产生新的消息时,根据客户端的 uid 从时间轮中获取客户端的管道,所以 MsgManager 类 “依赖“ 于 Wheel 类;

  • 消息模块与 http 服务之间没有直接关系,消息模块只是通过管道对象将消息传输给 http 服务而已。

明确了第一层的类关系,就可以对第二层的类关系进行展开分析和设计了。http 服务与消息模块留给大家进行设计,我们看时间轮模块如何设计。

对类内部的设计,需要从类对外提供的能力来入手分析。

通过对 http 服务、消息模块、时间轮模块三个角色之间的关系分析,我们知道时间轮对外提供了三项能力:

  • 对客户端管道对象的注册;

  • 对客户端超时信息的发送;

  • 根据客户端 uid 获取用户的管道。

注册客户端管道时,需要包括用户 uid、用户管道、注册时的时间轮刻度等信息;对客户端超时信息发送时,需要根据时间轮刻度找到用户管道;而获取用户管道时,需要根据客户端 uid 获取。所以,为了方便查询,用户 uid 与时间轮时间应该是一个双向映射的关系;同时,便于维护,将用户管道与时间轮时间封装成用户信息对象。

这里的管道对象,在不同的语言中可以采用不同的实现方式,比如 Go 语言中可以采用 chan ,Java 语言中可以采用队列。时间轮模块内部原理,在之前的文章(见《单体架构 IM 系统之长轮询方案设计》)中有详细描述,见下图。

时间轮模块类 Wheel 内部应包含三个类: 时间盘(TimeDisk)、用户信息映射(UserInfoMap)、时间轮刻度映射(TimeUserMap),这几个类之间的关系,见下图:

        

  • 时间轮对象 “拥有” 时间盘、用户信息映射和时间轮刻度映射三个对象,这三个对象组合成了时间轮对象,所以 Wheel 与 TimeDisk、UserInfoMap、TimeUserMap 是组合关系,离开了 Wheel,这三个对象毫无意义;

  • 时间盘 TimeDisk 对象,提供时间指针每秒走一格的能力;下一格的时间刻度所映射的所有客户端列表就是超时的客户端;

  • 用户信息映射 UserInfoMap 对象提供用户 uid 对用户信息对象的映射能力;

  • 时间轮刻度映射 TimeUserMap 对象提供时间轮时间刻度对用户 uid 列表的映射能力;

  • TimeDisk、UserInfoMap、TimeUserMap 三个对象之间没有任何关系。

我们继续对 UserInfoMap 设计第三层类关系,TimeDisk 和 TimeUserMap 留给大家分析和设计,见下图。

  • UserInfoMap “拥有” UserInfo,UserInfo “聚合” 成了 UserInfoMap,UserInfoMap 和 UserInfo 是聚合关系;

  • UserInfo 中封装了管道 UserChan 对象,UserInfo “拥有” UserChan 对象,两者是关联关系。

对业务模块的类关系,通常设计到第三层,整个类关系脉络应该就非常清晰了;http 服务端,将上述三层类关系进行整合,见下图。

在这个简单的整体类关系图中,依赖、关联、聚合、组合四种类关系全部进行了应用。明确了整个模块中关键类之间的关系后,剩下的就是对方法逻辑的编写了,这对于初级程序员来说就不是个事了!

最后,总结文中关键:

  1. 模块的详细设计,重点体现在需要设计几个具有明确职责的角色(类),以及角色(类)之间应该设计成什么样的关系;

  2. 模块的详细设计,应该像架构设计一样,由高到低,逐层进行;

  3. 类之间包括六类关系:依赖、关联、聚合、组合、继承和实现,在业务系统中以前四类为主:

    (1)依赖的核心是 “使用”;

    (2)关联的核心是 “拥有”;

    (3)聚合的核心是 整体与个体之间的 “拥有” 与 “聚合”,个体离开整体后对象的生命周期可以继续;

    (4)组合的核心是 整体与个体之间的 “拥有” 与 “组合”,个体离开整体后对象的生命周期结束;

  4. 对类内部的设计,需要从类对外提供的能力来入手分析;

  5. 基于时间轮方案实现的 http 长轮询中,关键类之间的关系如下:

    (1)HttpService 依赖 Wheel;

    (2)MsgManager 依赖 Wheel;

    (3)Wheel 组合 TimeDisk;

    (4)Wheel 组合 UserInfoMap;

    (5)Wheel 组合 TimeUserMap;

    (6)UserInfoMap 聚合 UserInfo;

    (7)UserInfo 关联 UserChan。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2025年美赛B题-结合Logistic阻滞增长模型和SIR传染病模型研究旅游可持续性-成品论文

模型设计思路与创新点: 建模的时候应该先确定我们需要建立什么类的模型?优化类还是统计类?这个题需要大量的数据分析,因此我们可以建立一个统计学模型。 统计学建模思路:观察规律,建立模型,参…

FireFox | Google Chrome | Microsoft Edge 禁用更新 final版

之前的方式要么失效,要么对设备有要求,这次梳理一下对设备、环境几乎没有要求的通用方式,universal & final 版。 1.Firefox 方式 FireFox火狐浏览器企业策略禁止更新_火狐浏览器禁止更新-CSDN博客 这应该是目前最好用的方式。火狐也…

关联传播和 Python 和 Scikit-learn 实现

文章目录 一、说明二、什么是 Affinity Propagation。2.1 先说Affinity 传播的工作原理2.2 更多细节2.3 传播两种类型的消息2.4 计算责任和可用性的分数2.4.1 责任2.4.2 可用性分解2.4.3 更新分数:集群是如何形成的2.4.4 估计集群本身的数量。 三、亲和力传播的一些…

使用 postman 测试思源笔记接口

思源笔记 API 权鉴 官方文档-中文:https://github.com/siyuan-note/siyuan/blob/master/API_zh_CN.md 权鉴相关介绍截图: 对应的xxx,在软件中查看 如上图:在每次发送 API 请求时,需要在 Header 中添加 以下键值对&a…

万物皆有联系:驼鸟和布什

布什?一块布十块钱吗?不是,大家都知道,美国有两个总统,叫老布什和小布什,因为两个布什总统(父子俩),大家就这么叫来着,目的是为了好区分。 布什总统的布什&a…

C++ ——— 仿函数

目录 何为仿函数 仿函数和模板的配合使用 何为仿函数 代码演示: class Add { public:int operator()(int x, int y){return x y;} }; 这是一个 Add 类,类里面有一个公有成员函数重载,重载的是括号 那么调用的时候: Add ad…

Charles 4.6.7 浏览器网络调试指南:流量过滤与分析(六)

1. 概述 在网络调试和优化过程中,Charles 不仅可以实现简单的网络抓包操作,还支持更高级的抓包技巧和流量分析功能。这些功能能够帮助开发者深入挖掘网络请求的细节,为复杂问题提供有效的解决方案。本文将重点讲解 Charles 的过滤规则、自定…

Haproxy入门学习二

一、Haproxy的算法 1.haproxy通过固定参数balance指明对后端服务器的调度算法,其中balance参数可以配置在listen或backend选项中 2.haproxy的调度算法分为静态和动态调度算法,其中有些算法可以根据参数在静态和动态算法中相互转换 3.静态算法&#xff1a…

程序地址空间

程序地址空间回顾(这真的是吗?) 我们之前学习C/C的时候是否听过:对于自己的C/C程序,我们默认认为自己的内存地址空间是: 代码区(Text Segment):存放程序的机器指令代码&…

【Numpy核心编程攻略:Python数据处理、分析详解与科学计算】1.21 索引宗师:布尔索引的七重境界

1.21 索引宗师:布尔索引的七重境界 目录 #mermaid-svg-Iojpgw5hl0Ptb9Ti {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-Iojpgw5hl0Ptb9Ti .error-icon{fill:#552222;}#mermaid-svg-Iojpgw5hl0Ptb9Ti .…

nvm安装详细教程(安装nvm、node、npm、cnpm、yarn及环境变量配置)

一、安装nvm 1. 下载nvm 点击 网盘下载 进行下载 2、双击下载好的 nvm-1.1.12-setup.zip 文件 3.双击 nvm-setup.exe 开始安装 4. 选择我接受,然后点击next 5.选择nvm安装路径,路径名称不要有空格,然后点击next 6.node.js安装路径&#…

【Matlab高端绘图SCI绘图模板】第006期 对比绘柱状图 (只需替换数据)

1. 简介 柱状图作为科研论文中常用的实验结果对比图,本文采用了3组实验对比的效果展示图,代码已调试好,只需替换数据即可生成相关柱状图,为科研加分。通过获得Nature配色的柱状图,让你的论文看起来档次更高&#xff0…

CTFSHOW-WEB入门-命令执行29-32

题目:web 29 题目:解题思路:分析代码: error_reporting(0); if(isset($_GET[c])){//get一个c的参数$c $_GET[c];//赋值给Cif(!preg_match("/flag/i", $c)){eval($c);//if C变量里面没有flag,那么就执行C…

【以音频软件FFmpeg为例】通过Python脚本将软件路径添加到Windows系统环境变量中的实现与原理分析

在Windows系统中,你可以通过修改环境变量 PATH 来使得 ffmpeg.exe 可在任意路径下直接使用。要通过Python修改环境变量并立即生效,如图: 你可以使用以下代码: import os import winreg as reg# ffmpeg.exe的路径 ffmpeg_path …

LCR 139.训练计划 I

目录 题目过程解法双指针法(两端开始)快慢指针 题目 教练使用整数数组 actions 记录一系列核心肌群训练项目编号。为增强训练趣味性,需要将所有奇数编号训练项目调整至偶数编号训练项目之前。请将调整后的训练项目编号以 数组 形式返回。 过…

《多阶段渐进式图像修复》学习笔记

paper:2102.02808 GitHub:swz30/MPRNet: [CVPR 2021] Multi-Stage Progressive Image Restoration. SOTA results for Image deblurring, deraining, and denoising. 目录 摘要 1、介绍 2、相关工作 2.1 单阶段方法 2.2 多阶段方法 2.3 注意力机…

21.2-工程中添加FreeRTOS(掌握) 用STM32CubeMX添加FreeRTOS

这个是全网最详细的STM32项目教学视频。 第一篇在这里: 视频在这里 STM32智能小车V3-STM32入门教程-openmv与STM32循迹小车-stm32f103c8t6-电赛 嵌入式学习 PID控制算法 编码器电机 跟随 **V3:HAL库开发、手把手教学下面功能:PID速度控制、PID循迹、PID跟随、遥控、…

gitee——报错修改本地密码

有时候当我们向远端push本地的仓库时会有一些报错的行为。 如下: 这是因为我们在gitee修改了密码时,本地还没有更新提交,总是报错 解决修改密码报错 如下: 1.在本地点击搜索栏找到控制面板 步骤如下

联想Y7000+RTX4060+i7+Ubuntu22.04运行DeepSeek开源多模态大模型Janus-Pro-1B+本地部署

直接上手搓了: conda create -n myenv python3.10 -ygit clone https://github.com/deepseek-ai/Janus.gitcd Januspip install -e .pip install webencodings beautifulsoup4 tinycss2pip install -e .[gradio]pip install pexpect>4.3python demo/app_januspr…

批量卸载fnm中已经安装的所有版本

直接上代码 fnm list | awk -F NR>1 {print line} {line$2} | xargs -n 1 -I {} fnm uninstall {}原理 fnm list 列出 fnm 中所有已经安装的 node 版本 awk -F NR>1 {print line} {line$2} 以空格分隔-F {line$2},取从左到右第 2 段(v22.11…