软件项目一般经过了以下流程:
需求分析, 方案设计, 编码, 单元测试, 集成测试, 系统测试, 最后到达发布和维护阶段。
当到达最后的阶段时, 依然会有很多编码的任务, 如新需求的开发, 如Bug修复等. 这个就是今天谈及的问题. 怎样让项目容易维护?
老生常谈的软件特点是: 易用性, 可维护性, 以及可扩展性。
前者是从用户的体验角度来说明, 后两者讲的是项目的维护扩展问题: 易用性是UI设计者需要考虑的问题. 可维护性和可扩展性, 是软件工程师需要面对的问题.
特别的是, 大部分的团队是项目导向型的, 而非产品导向型。 (项目导向, 指团队不断的从外部接项目的方式运作,产品导向, 是指团队有固定产品的开发, 长时间只负责一两个产品的研发), 一般而言, 90%以上的团队都属于项目导向型的团队。这样就涉及两个软件项目之间存在相似性的维护问题。
谈及项目的可维护性和可扩展性, 下面有几个案例:
案例一: 某公司从事GPS开发. 产品既包括欧美版本, 也包括亚洲版本, 显然欧美和亚洲的用户操作习惯是不同的. 并且这两个版本是需要分别打包发布的. 其中亚洲版本和欧美版本共同性有60%, 差异性有40%。 如何处理这样具有相似的特性。
案例二: 某公司从事通信系统的数据分析项目, 要求对2G通信系统和3G通信系统中, 产生的数据进行详尽分析, 从而对分析这些系统中的流程是否正确, 通信系统的质量和完整性进行有效的分析. 其中2G网络和3G网络存在大量的类似需求, 相似逻辑。 要求代码可维护, 该如何处理?
回顾下上面的案例一和案例二, 有下面的特点, 很强的相似性, 用图表示如下:
图一: 简单的项目中功能相同的情况
以项目导向型的团队, 面临的真实情况, 应该更加复杂一些:
图二: 通常情况下项目间的功能相似情况
当项目更多的时候, 这个图将更加复杂。这个并不夸大, 比如:
- 解析xml
- 建立socket连接
- 线程池
- 数据库连接
更多的是GUI的项目, 到处都会遇到类似的弹出窗口, 输入窗口, 进度条等等。
这些工作有很多SDK平台,或者是开源项目已经帮我们实现了。这个问题我们暂时先不深究。后面我们回过头来再借鉴这样的思路。
上面谈到的相似功能, 基本上是脱离业务的功能, 有些团队,面临更多的业务上的相似, 以上面提到的GPS项目为例, 涉及到的业务相似功能有:
- 根据地名进行检索
- 设置固定图层的功能
- ....
上述通信项目中, 跟业务相关的相似功能有:
- 捕获的数据展示
- 二进制文件解析
- 匹配算法和模式
- 匹配结果展示
- ...
大部分项目团队, 如果出现了相同的功能, 采用的方式是复制而不是复用, 正如图一和二的方式, 他们是分别管理的。这样带来至少以下隐患:
1. 花费大, 同一个功能要为各个项目重复开发一遍
2. 为了避免第一个问题, 大部分项目成员会用copy的方式, 这个带来第二个问题, 一个项目修正了Bug, 需要在另外一个项目中再Copy过去, 也就是要维护多份相同的代码.
3. 增加新进的成员的学习压力. 重复维护多分代码, 需要熟悉多个项目的架构, 以及通过经验, 了解copy的位置以及Copy缘由。最后项目对团队的某些"有经验的员工"产生极大的依赖。
4. 软件开发最终成为一种强体力劳动。
为了避免这样的问题, 下面有一个改进的结构
图四: 容易维护的项目平台的搭建
这个架构的思路是:
1. 注意这里有两种方向的划分
- 横向划分, 即分层, 按照两种维度(业务无关的用户API层, 以及业务相关的插件层)让相似的代码能被较好的维护或重用.
- 纵向划分, 业务插件层有这样的划分, 这个是为了让业务功能的代码尽量的降低耦合性, 这样, 保证项目中, 用不到的功能, 直接从业务插件层中排出出来, 避免项目中无关代码过多.
2. 所有项目以平台为基础, 进行分层架构. 彻底消除相似性的代码各自为政的问题.
结合实际的项目来看看, 比如大众需要设定两个项目, 越野车项目A, 货运大卡车项目B. 看各层如何分配:
SDK API层: 这个可以找螺丝生产商, 提供标准化生产的X型号的螺纹, 螺帽, 齿轮, 橡胶等, 该层是业界标准层
Uer API 层: 这个是对底层封了一遍, 提供的是大众独有的API, 比如轴承, 滚轮等, 该层是公司标准层, 与业务无关
业务插件层: 这层是对User API再次封装, 形成了跟汽车业务相关的层面, 但跟具体的项目没有关系, 比如脚踏板,刹车, 轮胎
项目层: 这层跟具体项目有关系, 比如越野车的座位, 后备箱这类功能, 货运车的斗篷, 拖箱等等. 这个层面,只能被具体的项目使用。
好处是, 当现在需要定制一个大众的长途汽车, 业务层插件可以使用, UserAPI层的插件可以重复使用。 更重要的是, 大众的公用业务插件升级,或者UserAPI升级了, 后续生产的车种, 都直接升级。 不需要各自升级。
参考资料
参考一: 摘自《软件架构设计》 2.1.1 关注点分离
选择上述图片的原理是, 维度划分比我想到的更精准. 我所提到的业务无关, 这边只的是通用, 业务相关, 这边指的是专用。 中间还划分成领域通用部分, 考虑很细致和周全。特此推荐下这个图片。