当前位置: 首页 > article >正文

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

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

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

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

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

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

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

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

依赖

依赖方与被依赖方是 “使用” 关系,从代码上看,被依赖方对象往往出现在依赖方对象的方法内部。比如:依赖方是 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.kler.cn/a/524622.html

相关文章:

  • web集群
  • three.js用粒子使用canvas生成的中文字符位图材质
  • 【apt源】RK3588 平台ubuntu20.04更换apt源
  • (开源)基于Django+Yolov8+Tensorflow的智能鸟类识别平台
  • 可爱狗狗的404动画页面HTML源码
  • AI软件外包需要注意什么 外包开发AI软件的关键因素是什么 如何选择AI外包开发语言
  • 用户创建命令的详细使用与参数说明
  • 深度学习每周学习总结R5(LSTM-实现糖尿病探索与预测-模型优化)
  • Origami Agents:通过AI驱动的研究工具提升B2B销售效率
  • 网络工程师 (8)存储管理
  • docker运行Open-Webui 界面化展示 deepseek-r1大模型
  • 【新春特辑】2025年春节技术展望:蛇年里的科技创新与趋势预测
  • CUDA学习-内存访问
  • 飞鸟小目标检测数据集VOC+YOLO格式1657张2类别
  • 解锁豆瓣高清海报:深度爬虫与requests进阶之路
  • Kubernetes 环境中的自动化运维实战指南
  • 【灵蛇献瑞】| 2024 中国开源年度报告正式发布!
  • leetcode hot 100 搜索二维矩阵II
  • 详解:网站地图对快速收录的重要性
  • Ansys Maxwell:初始电压和击穿电压计算
  • P11468 有向树
  • ProfibusDP主机与从机交互
  • AI提示词(Prompt)入门详解
  • 项目集成GateWay
  • js中的保护对象
  • MATLAB算法实战应用案例精讲-【数模应用】方向梯度直方图(HOG)(附python代码实现)