功能模块划分是架构师的“核心技能”和“基本职责”——通过将系统拆分为功能模块,实现模块内高内聚、模块间松耦合,使大型系统变得易于管理。
正如Hassan Gomaa所言:“要成功应对大型软件系统的固有复杂性,必须提供一种将系统拆分为子系统的方法…通过拆分并仔细定义子系统之间的接口,每个子系统可以独立设计。”(本质上是控制复杂度)
本文通过功能树方法和酒店管理系统实战案例,帮助你掌握“从需求到设计”的模块划分方法。
通俗理解:功能树如同一张“功能清单树”,将系统要实现的功能按照“功能类别→功能组→功能项”的层次结构组织起来,让你一目了然地看到系统的所有功能。
核心作用:功能树是需求分析的框架工具,帮助分析师逐层选择和确定系统必须具备的功能(Function)和特性(Feature),是需求分析阶段的成果。
核心要点的三个要点:
功能树示例(呼叫中心系统):
呼叫中心系统
├── 自动服务
│ ├── 自动语音服务
│ │ ├── 最新公告
│ │ ├── 用户自助管理
│ │ └── 用户留言
│ ├── 自动传真服务
│ │ └── 发送传真服务
│ │ ├── 在线传真
│ │ └── 离线传真
│ └── 接受传真服务
├── 人工服务
│ ├── 被动服务
│ │ ├── 技术咨询
│ │ ├── 服务与产品购买
│ │ └── 投诉建议
│ └── 主动服务
│ ├── 电话营销
│ └── 满意度调查
└── 其他功能
├── 系统管理
└── 知识管理
通俗理解:功能分解像“列购物清单”(要买什么),结构分解像“规划购物路线”(怎么去买)。两者完全不同,但很多人容易混淆。
核心作用:理解功能分解和结构分解的区别,是正确进行模块划分的前提。功能分解关注“做什么”(问题域),结构分解关注“怎么做”(解决方案)。
核心要点的三个要点:
问题域 vs 解决方案示意图:
需求
↓
问题领域(功能树:功能分解)
↓
软件架构(转换桥梁)
↓
解决方案(功能模块结构图:结构分解)
↓
软件系统描述“系统如何运作”,是架构设计的成果,由架构师自行设计。
从功能树到功能模块结构图,需要架构师进行“设计转换”——分析功能之间的实现关系,将业务上紧密关联的功能组映射到功能模块,实现“高内聚、低耦合”。
就像“把相关的工具放在同一个工具箱”——业务上紧密关联的功能,实现时通常涉及相同的类和数据,应放在同一个模块中。
将“功能组”映射到“功能模块”,是实现模块内高内聚、模块间低耦合的核心方法。通过分析功能的实现关系,将业务上紧密关联的功能组划分到同一个功能模块。
原理:业务上紧密关联的一组功能,实现时通常涉及相似的类、相似的数据结构。因此,将“功能组”映射到“功能模块”,可以实现模块内的高内聚(功能相关、实现相关)、模块间的低耦合(不同模块的实现相对独立)。
为什么这样设计?因为功能的实现不是由单个类完成的,而是由多个相互协作的软件元素共同完成的。如果业务上紧密关联的功能涉及相同的软件元素,将它们放在同一个模块中,可以让这些元素共享,减少重复代码,提高内聚度。
深层原因:软件设计的核心目标是“高内聚、低耦合”。高内聚意味着模块内的元素紧密相关,低耦合意味着模块间的依赖关系简单。通过将功能组映射到功能模块,可以实现这个目标。
功能组:功能树中业务上紧密关联的功能集合(如“办理预定”、“办理入住”、“办理退房”都是酒店前台服务相关的功能)。
功能模块:架构设计中实现这些功能的模块(如“宾客服务”模块)。
映射逻辑:分析功能的实现关系,如果一组功能涉及相同的类、相似的数据结构,就将它们映射到同一个功能模块。
为什么这样映射?因为这样可以实现代码复用(相同的类可以被多个功能共享)、降低耦合(不同模块的实现相对独立)、便于分工(一个模块可以分配给一个开发小组)。
通用模块:一些公共服务(如错误报告、日志记录、安全验证)同时支持多组功能的实现,不属于任何单一“功能模块”。
为什么需要分离?因为这些公共服务是多个功能模块都需要的基础设施,如果放在某个功能模块中,会导致其他模块依赖这个模块,增加耦合度。
如何分离?将这些公共服务独立模块化,放入对应的“通用模块”或“通用机制”中,让所有功能模块都可以使用,但不依赖特定的功能模块。
酒店管理系统
├── 功能模块(4个)
│ ├── 资产管理模块
│ ├── 宾客服务模块
│ ├── 人员考核模块
│ └── 系统维护模块
└── 通用模块(1个)
└── 角色与权限管理框架
功能树(问题域) 功能模块结构图(解决方案)
───────────────── ──────────────────────
宾客服务(功能组) → 宾客服务模块
├── 办理预定 ├── Reservation UI
├── 办理入住 ├── Checkin UI
└── 办理退房 ├── Checkout UI
├── ServiceManager
├── PayManager
├── Reservation
└── Room
功能树像“需求清单”,需要从各种渠道收集,然后检查是否完整、是否准确。
获得功能树是模块划分的第一步,评审功能树是确保需求完整准确的关键。只有获得准确完整的功能树,才能正确划分功能模块。
三种方式:获取文档、沟通交流、分析产品。
获取文档:《软件需求规格说明书(SRS)》是最可能出现功能树的地方,因为SRS会系统化地描述功能,其“章-节-小节”结构往往就体现了功能树。更上游的文档如《愿景文档》《方案建议书》也可能包含功能树。
沟通交流:与业务人员、需求分析师沟通,收集功能信息,然后自己组织成功能树。
分析产品:分析本公司或竞争对手的产品(包括文档、市场材料、UI界面),提炼功能树。
为什么需要多种方式?因为功能树可能出现在不同的地方,单一渠道可能不完整。通过多种方式收集,可以确保功能树的完整性。
功能树 vs 页面流转图:功能树是功能分解结构,描述“系统要做什么”;页面流转图是页面跳转关系,描述“用户怎么操作”。页面流转图不能作为功能树,因为它描述的是交互流程,不是功能分解。
判断标准:功能树的上层节点是更高层次的“功能组”,下层节点是“子功能”,上下层节点之间是“隶属关系”和“支撑关系”。页面流转图描述的是“页面跳转关系”,不是功能分解关系。
为什么必须区分?因为如果用页面流转图作为功能树,会导致模块划分错误——按照页面跳转关系划分模块,无法实现“高内聚、低耦合”。
? 页面流转图(不是功能树):
系统登录
├── 数据编辑
│ ├── 第一个
│ ├── 前一个
│ ├── 下一个
│ └── 添加/编辑/删除
├── 数据查询
│ ├── 搜索
│ └── 全部
└── 系统维护
├── 系统数据转换
└── 修改系统密码? 功能树(真正的功能树):
设备管理系统
├── 设备管理(功能组)
│ ├── 录入设备记录(功能项)
│ ├── 维护设备记录(功能项)
│ └── 维护维修记录(功能项)
├── 系统管理(功能组)
│ ├── 组织结构管理(功能项)
│ └── 设备类别管理(功能项)
└── 设备统计查询(功能组)
├── 维修统计查询(功能项)
└── 调拨统计查询(功能项)两个标准:用户导向、全面覆盖。
用户导向:功能树应该反映用户价值,从用户视角组织功能,而不是从技术视角。如果功能树从技术视角组织(如“金额计算”、“按键处理”),说明没有理解用户需求。
全面覆盖:功能树应该全面覆盖系统功能,不能遗漏重要功能。如果功能树遗漏了关键功能(如“故障报警”、“现金箱控制”),说明需求分析不完整。
因为功能树是模块划分的基础,如果功能树不精确、不全面,后续的模块划分就会出错。评审功能树可以及时发现需求问题,避免设计阶段的重复工作。
自动售货机管理系统
├── 金额计算
│ ├── 金额累加
│ ├── 清除现有金额
│ └── 计算退币金额
└── 按键处理
├── 按键按下处理
└── 点亮可售货等待
问题:
项目:某酒店开发酒店管理系统,需支持前台办理预订、办理入住、办理退房,经理进行人员考核,管理员进行系统维护等功能。
需求文档:提供了用例图,展示了不同角色的功能需求:
问题:如何将这些功能需求转化为功能模块划分?如何实现"高内聚、低耦合"?
目标:从需求文档中获取功能树,明确系统要提供哪些功能。
核心操作要点:
要点1:从用例图提取功能
分析用例图,识别所有功能项:前台功能(办理预订、办理入住、收取押金、办理退房)、经理功能(人员考核)、管理员功能(系统维护)。
为何从用例图提取?因为用例图描述了系统要提供的功能,是功能树的重要来源。
要点2:组织成功能树结构
按照"功能类别→功能组→功能项"的层次组织功能。
对于酒店管理系统,可以组织为:
要点3:补充通用功能
识别所有角色都需要的基础功能:登录、退出、修改密码。
这些功能不属于任何单一功能类别,应作为通用功能处理。
为何如此设计?
从用例图提取:因为用例图是需求分析的标准产出物,描述了系统要提供的功能,是功能树的重要来源。
组织成树状结构:因为功能之间存在层级关系,树状结构能够清晰地表达这种关系,便于后续的模块划分。
补充通用功能:因为有些功能是多个角色都需要的基础功能,如果不单独识别,会导致后续模块划分时不知如何处理。
目标:确保功能树用户导向、全面覆盖,为模块划分提供准确的基础。
核心操作要点:
要点1:检查用户导向
检查功能树是否从用户角度组织功能,而不是从技术角度。
好的功能树:从业务角度组织(如"宾客服务"、“人员考核”),用户能够理解。
差的功能树:从技术角度组织(如"金额计算"、“按键处理”),用户无法理解。
为何必须用户导向?因为功能树是需求分析的产出物,应反映用户价值。如果从技术角度组织,说明未理解用户需求,后续的模块划分也会出错。
要点2:检查全面覆盖
检查功能树是否涵盖了所有重要功能,无遗漏。
对于酒店管理系统,需检查:是否遗漏了"故障报警"、"现金箱控制"等关键功能?是否遗漏了"加载人员"的功能?是否遗漏了"资产管理"相关功能?
为何必须全面覆盖?因为功能树是模块划分的基础,如果遗漏了重要功能,后续的模块划分会不完整,导致系统功能缺失。
要点3:区分功能树和页面流转图
确保获得的是功能树(功能分解结构),而非页面流转图(页面跳转关系)。
功能树:描述"系统要做什么",按功能类别组织。
页面流转图:描述"用户如何操作",按页面跳转关系组织。
为何必须区分?因为页面流转图不能作为功能树,用它划分模块会导致模块划分错误。
为何如此设计?
检查用户导向:因为功能树应反映用户价值,从用户角度组织功能。如果从技术角度组织,说明需求分析有问题。
检查全面覆盖:因为功能树是模块划分的基础,如果遗漏了重要功能,后续的模块划分会不完整。
区分功能树和页面流转图:因为两者描述的内容不同,混淆会导致模块划分错误。
目标:将功能树中的功能组映射到功能模块,实现"高内聚、低耦合"。
核心操作要点:
要点1:分析功能的实现关系
分析每个功能的实现涉及哪些类、哪些数据结构。
对于酒店管理系统:
为什么分析实现关系?因为功能的实现不是由单一类完成的,而是由多个相互协作的软件元素共同完成的。如果业务上紧密关联的功能涉及相同的软件元素,应该将它们放在同一个模块中。
要点2:将功能组映射到功能模块
将业务上紧密关联的功能组映射到同一个功能模块。
对于酒店管理系统:
为什么这样映射?因为这样可以实现模块内的高内聚(功能相关、实现相关)、模块间的松耦合(不同模块的实现相对独立),还便于将这组功能分配给一个程序小组负责开发。
要点3:分离通用模块
识别所有功能模块都需要的基础服务,将它们独立为通用模块。
对于酒店管理系统:
为什么需要分离?因为这些公共服务是多个功能模块都需要的基础设施,如果放在某个功能模块中,会导致其他模块依赖这个模块,增加耦合度。独立为通用模块,让所有功能模块都可以使用,但不依赖特定的功能模块。
为什么这么设计?
第四步:验证模块划分结果
目标:验证模块划分是否实现了“高内聚、松耦合”的目标。
核心操作要点:
要点1:检查模块内聚度
检查每个功能模块内的功能是否业务相关、实现相关。
对于“宾客服务”模块,检查“办理预定”、“办理入住”、“办理退房”这些功能是否涉及相同的类(如Room、Reservation),是否可以共享实现(如ServiceManager、PayManager)。
为什么检查内聚度?因为高内聚意味着模块内的元素紧密相关,如果模块内的功能不相关,说明模块划分不合理。
要点2:检查模块耦合度
检查不同功能模块之间的依赖关系是否简单。
对于酒店管理系统,检查“宾客服务”模块是否依赖“人员考核”模块?如果依赖,说明耦合度高,需要调整。
为什么检查耦合度?因为松耦合意味着模块间的依赖关系简单,如果模块之间相互依赖,说明模块划分不合理。
要点3:检查通用模块
检查通用模块是否被所有功能模块使用,但不依赖特定的功能模块。
对于“角色与权限管理”通用模块,检查是否所有功能模块都可以使用,但不依赖特定的功能模块。
为什么检查通用模块?因为通用模块应该被所有功能模块使用,但不应该依赖特定的功能模块,否则会增加耦合度。
为什么这么设计?
总结收尾
通用应用逻辑功能模块划分的通用逻辑可以总结为:“获得功能树→评审功能树→分析实现关系→映射功能模块→分离通用模块→验证划分结果”。
通俗比喻:就像“整理工具箱”——先列出所有工具(获得功能树),检查工具是否齐全(评审功能树),分析哪些工具经常一起用(分析实现关系),把相关的工具放在同一个工具箱(映射功能模块),把通用工具单独放(分离通用模块),最后检查工具箱是否整理好了(验证划分结果)。
可落地的交付物
功能模块划分检查清单:
最简操作模板:
功能模块划分三步走:
1. 获得功能树:从需求文档、沟通交流、分析产品中获得功能树
2. 评审功能树:检查用户导向、全面覆盖,区分功能树和页面流转图
3. 划分功能模块:分析实现关系→映射功能模块→分离通用模块→验证划分结果
记住
模块划分是架构师的“关键技能”和“基础职责”。借助功能树方法,将需求分析的功能拆分,转化为架构设计的结构拆分,达到“高度内聚、低度耦合”的目标,使大型系统更易于管理。
扫码加好友,拉您进群



收藏
