加载中...

本篇要回答的问题:需求分析之后的架构第二步——系统的概要设计怎么做?评判系统分解好坏的标准是什么?为什么桌面程序会沉淀出 MVC 这个套路?

第一章讲过需求分析(架构第一步,占三分之一以上精力)。本讲把话题拉回架构,讲架构第二步:系统的概要设计(系统设计)。它的核心能力就是"对系统进行分解"——明确子系统的职责边界和接口协议,把大框架搭起来。

桌面开发篇的内容地图

基础架构与业务架构

维度 是什么 考验什么 备注
基础架构 技术选型:选 OS、语言、框架、第三方库 选择能力、技术前瞻性与判断力 影响面更广、选错代价更高;真正牛的架构师无比重视它(如"大中台、小前台")
业务架构 业务系统的分解能力 对领域问题的理解 分解领域问题,避不开需求分析

两者本质都是对业务系统的分解:基础架构分解出"与业务几乎无关、领域无关的基础设施",业务架构则分解领域问题本身。

系统分解优劣的两条朴素评判标准

标准 含义
接口符合自然预期 功能的使用界面(接口)应尽可能符合业务需求对它的自然预期
高内聚低耦合 功能实现要高内聚,功能与功能之间耦合尽可能低

软件系统的组织单元有多层:子系统 → 模块 → 类 → 方法/函数。每一层的分解都遵循相同的方法论

接口要自然体现业务需求

各类组织单元的"使用界面"是什么:

组织单元 使用界面(接口)
函数 函数原型:函数名(含名字空间全称)+ 输入参数列表 + 输出结果列表
公开属性列表 + 公开方法列表(方法比函数多一个 receiver/this/self)
包/静态库 由编程语言定义,对开发者友好
动态库(Go 里叫 plugin) 由操作系统定义,可跨语言但只能取语言间共性,对开发者不友好
网络服务程序(service) 网络协议
命令行程序(CLI) 命令行(命令名/开关/参数)+ stdin + stdout
桌面程序(GUI) 用户的操作方式,最重要的是交互范式(故需产品经理定义)

画图服务端的网络协议(service 的使用界面)

子系统是唯一不存在物理实体、只活在架构文档里的概念——它是一个逻辑上的"大模块",同样要定义使用接口。它与模块的对应有两种:

情况 结构 子系统的使用接口
最常见 一个根模块(总控)+ 若干子模块 即根模块的使用接口 ——
另一种 多个相似模块构成 这些模块有统一的使用界面 Office 的 IO 子系统:Word/HTML/TXT/PDF 读写

关键判断:一个程序员系统分解能力强不强,一眼就能看出来——不用看实现,只看他定义的模块/类/函数的使用接口。若存在大量说不清业务意图的函数、职责不清的模块,基本还在搬砖阶段。职责是否单一清晰、接口是否自然到无需额外文档,体现架构功力。

功能实现准则:高内聚低耦合

准则 含义 许式伟的习惯/关注点
高内聚 一个功能的代码尽量写在一起,不散落各处 一功能尽量独立一个文件;小功能合并时用 // ---- 注释分割成逻辑"小文件"。好处:多大团队协作都顺畅,提交基本不冲突
低耦合 实现某功能所依赖的外部环境少、易构建 见下

外部依赖分两种:

依赖类型 关注点
业务无关的基础组件依赖 核心看稳定:① 成熟度(诞生多久、接口是否稳定、issue 是否少);② 持久性(维护者社区信用、项目活跃度、贡献者多少)
底层业务模块依赖(架构关注重点) ① 依赖要"通用",别让底层为我定制接口;② 依赖的接口个数少、调用频次低

怎么做系统分解?

系统分解是领域性问题,没有放之四海皆准的办法。流程:从需求归纳出发 → 把功能点涉及的数据(对象)、操作接口理清并归纳 → 每个功能归类 → 理清类与类关系、做到逻辑自洽 → 基本系统框架成形。

概要设计阶段以子系统为维度阐述各角色关系;对关键子系统进一步分解,甚至确定其所有模块的职责与接口。但:

焦点不是确定完整的模块列表,而是整个系统如何被有效串联起来。某个子系统不细化也无项目风险,就不必在此阶段细化。

而且概要设计也应该有代码产出,目的有二:① 系统初始框架代码(架子搭起来);② 原型性代码验证(核心子系统提供 mock)。好处是一上来就消除全局系统性风险,并给各负责人具象、确定的认知。

代码即文档。代码是理解一致性更强的文档。

再谈 MVC:套路从何而来

桌面虽业务千差万别,但本身是确定性领域,会形成固有套路——MVC。不同时期交互不同(键鼠/触屏),但框架一致:事件分派做输入,GDI 做界面呈现

桌面框架:事件分派输入 + GDI 呈现

用"稳定点/变化点"解释 MVC 为何成形:

承接的稳定点/变化点 形态
Model 业务核心逻辑(稳定);其变化点在存储和网络,但只变实现、接口不变,故 IO/网络子系统也稳定,归入 Model 类与函数,是一个整体
View 承接屏幕尺寸变化点(信息组织效率) 不同尺寸/平台可有不同实现,但数量不多(响应式鼓励共享)
Controller 承接交互方式变化点(鼠标 vs 多点触摸),把输入转为对 Model 的业务请求 插件化,各 Controller 非常独立,按平台初始化不同实例

变种如 MVP:Model 发出 DataChanged 后由 Controller 监听并 Update View(而非 View 自己响应)。这些差异只是细节权衡,不改实质。

怎么看待实战?

学架构强调"做中学"——计算机科学是实践科学,架构经验是一线实战的积累。为看清架构演变,作者还实现了一个所有代码揉在一起的非 MVC 版本(v01,约 470 行)与 MVC 版本(v26)对比:

对比项 v01(非 MVC) v26(MVC)
代码规模 约 470 行 约 580 行(多 110 行:dom.js 100 + view.js 112 + 4 个 Controller + index.htm 18)
价值 —— 不是降代码量,而是让团队并行:拆 6 文件可交 6 人,人均约 100 行

关键判断:MVC 的价值不在减少总行数,而在让团队协同、工作并行——靠的正是"功能高内聚、功能间低耦合"。代码量越大、迭代越频繁,分工的必要性越大。

总结

概要设计(系统设计)是架构第二步,核心是"对系统分解"。判好坏看两条:接口自然体现业务需求、实现高内聚低耦合。各级组织单元(子系统/模块/类/函数)的分解遵循同一方法论,使用界面是关键观察点。阶段焦点是"系统如何被有效串联"而非穷举模块,且应有框架代码与 mock 产出以提前消除全局风险。桌面领域的固有套路 MVC,正是用稳定点/变化点对业务核心、屏幕尺寸、交互方式分别安放的结果。

先把"是什么"回答清楚

概念 一句话说明
基础架构 做技术选型(OS/语言/框架/库),考验选择能力,影响面最广
业务架构 分解领域问题,离不开需求分析
使用界面(接口) 函数原型/类公开成员/网络协议/命令行/交互范式等,是分解的关键观察点
子系统 只存在于架构文档的逻辑大模块,由根模块或多个相似模块构成
高内聚低耦合 功能代码聚在一起、对外部依赖少且通用
概要设计的代码产出 初始框架代码 + 核心子系统 mock,用于提前消除全局风险
MVC 桌面领域用稳定点/变化点分解出的固有套路

一句话速记

概要设计就是"分解系统":用接口自然 + 高内聚低耦合两把尺子衡量分解质量,以子系统为维度先把骨架和 mock 搭出来消除全局风险——而 MVC 不过是桌面领域里"稳定点归 Model、屏幕变化归 View、交互变化归 Controller"的标准答案。

几条值得记住的判断

  • 看接口就能判断分解功力:说不清业务意图的函数、职责不清的模块,是搬砖信号。
  • 概要设计要出代码:框架 + mock 提前暴露并消除全局系统性风险。
  • MVC 不省代码量,省的是协作成本:让大团队高度并行。

思考题

对比 v01(所有代码揉一起)和 v26(MVC):从"功能高内聚、功能间低耦合、减少全局变量为控件化做准备"三个角度,你能推演出 v26 用 MVC 分解到底带来了哪些具体收益?回到你的项目,有没有某个职责不清、说不清业务意图的模块,正是分解没做好的痕迹?

公告栏
这是我的个人知识库。
记录技术,也记录生活 —— 读过的、试过的、想明白的,都堆在这儿。
最新文章
网站资讯
文章数目 :
5
已运行时间 :
本站总字数 :
15.7k
本站访客数 :
本站总访问量 :
最后更新时间 :
全局知识图谱
当前页面 已访问 文章 标签
ESC 关闭 · 滚轮缩放 · 拖拽移动 · Ctrl+G 开关