Windows应用软件开发,会有很多常用的模块,比如数据库、配置文件、日志、后台通信、进程通信、埋点、浏览器等等。下面是目前我们公司windows梳理的部分组件,梳理出来方便大家了解组件概念以及依赖关系:
每个应用里,现在或者以后都可能会存在这些模块。以我团队开发的全家桶为例,十多个应用对后台访问,就会有十多个重复的模块。后台通信的设计、开发、BUG修复,都是重复工作量。
业务/通用组件:通用模块,流程/逻辑基本是固定不变的,变的部分是上层业务调用,所以不变的部分我们需要把它固化下来、稳定下来,减少代码的复用、提高共性代码的稳定性、提升项目/人员的开发效率。
框架组件:除了通用模块,还有一些流程比较复杂。比如软件启动时很多业务的处理,需要框架来梳理、组织好业务流程,使其业务模块分层合理、依赖关系清晰。
所以我们抽取/开发了一些组件,以后还会有更多的组件。组件的开发/维护,大家都会参与,所以需要同步一些如何做好、做稳的概念。
组件调用方
组件尤其是通用组件,我们能支持更多方向的,一定要提供好支持。站在其它开发人员,考虑更多应用、业务的使用及场景,有这些思维,会让你的组件设计更加完善、可用性更高。
- 开发人员 - 自己、其它开发小伙伴、外部第三方开发人员
- 各个应用软件 - 内部软件、ISV以及ODC软件
- 业务场景 - 会议、医疗、教育等
当然,我们设计组件时也要掌握一个度,没有设计或者过度设计都不是我们的初衷。所以按照初衷-要尽可能提高效率(团队效率),即易维护、易阅读理解以及易扩展等,组件设计的方向也就不会有大问题。
组件设计原则
组件是应用软件开发的基石,组件不稳定,应用不可能稳定下来。所以我们需要考虑怎么做好组件。
如何把组件设计好,怎么评估组件的好坏?组件设计评估,有哪些维度
结合我的工作经验,吸收到的知识点,以下是我个人的一些总结:
1.所见即所得
"所思即所见,所见即所得",这是佛学里的思维。
我们软件设计也有这个"所见即所得"的原则,看见什么就是什么,执行的逻辑就是看到的方法/协议名称。
组件、接口及方法,应该是只做名称对应的实现。不得包含未知的逻辑,比如接口里同时存在Get、Check,在Get方法内调用了Check。又比如执行元素的缩放,缩放方法中不应该存在旋转逻辑如设置角度。
也不得只实现部分逻辑,比如清空用户缓存,结果内容只执行了部分路径下的数据清空。
所见即所得,关键点是:
- 对外的定义,要符合业务的第一性原理,回到组件设计的初衷,该有的功能/逻辑一个也不能少。
- 内部的逻辑,围绕着协议名称去实现,要覆盖且不脱离原有的初衷。
另外,solid中的单一职责原则,也是"所见即所得"原则中的一部分概念,设计要清晰、单一。
2.简单
简单,意思是对外要简洁、单一。所有的组件,都是给自己以及其它开发人员使用的,给各个应用调用的,那我们就需要考虑使用成本以及学习成本。
调用组件时,能够以最快的速度理解接口/组件的使用,以最快的速度完成组件的调用,这是组件开发人员需要考虑的。如果给出的接口以及调用流程太过复杂、混乱,调用时不得不花很多时间沟通、理解如何调用;项目上也会因为调用流程复杂,导致业务逻辑复杂,降低后续的可维护性、提高BUG定位的复杂性。
所以我们设计组件/接口,需要:
- 尽量少暴露内部实现。
- 不需要开放的类要隐藏
- 外界不需要关心的属性要设置internal
- 外界用不到的方法,设置internal。或者删除对外暴露接口里的冗余方法
- 实现要简单
- 对外暴露的接口/方法,尽可能的减少数量,能合并的合并
- 对外暴露的接口/方法,尽可能的减少参数,以最少参数实现相应功能
用一句话总结,就是最少知识原则(迪米特原则)
对外暴露越少越好,暴露的少肯定能减少耦合。我们常提的封装就是这个意思,高内聚低耦合,内部实现复杂的逻辑,外部减少耦合。
3.完整
完整是指组件,要提供一整套完整的功能,能全部覆盖对应场景,不能开发一部分然后提供出去使用。
- 尽可能的将应用内与这个组件相关的通用代码,挪到组件内
- 尽可能的满足后续业务的需要。不管是新增需求、新增应用、新增场景,后续都应该尽量无需新增代码,即可满足业务需求
比如,我们做OTA组件,那你应该把升级的所有实现放在OTA组件里。一个组件做一类的事情,但这个组件一定要把这一类的事情做完整。完整的意思是,在应用使用OTA组件时,能通过组件完成所有OTA的相关操作。比如检验是否需要升级,这段逻辑如果放在应用层去做,那就会存在冗余的重复代码,说明OTA组件未设计完整、没有实现完整。
当然如果你这样实现内部实现过多,那可以拆分一些独立的组件,如双网卡可以拆分为双网卡使用组件、双网卡修复组件。
完整,意味着可用性高,这一类场景的组件能支撑更多的业务。
4.易维护
易维护指的是,组件内部的代码实现以及设计应该容易维护。开发通用组件,后续可能会有BUG,如何保证定位问题、解决问题的高效呢?那就需要内部清真的代码实现、清晰的代码设计。如果一堆不安全代码、实现流程复杂、依赖关系混乱,肯定很难定位问题根因,修改代码后容易牵一发动全身,如果是后面的开发人员,估计他会吐N多口水。更不用说业务组件了,业务组件如果内部不易维护,BUG肯定一堆又一堆。
如何提升代码的可维护性?可以从以下几个方面着手:
- 代码实现要简洁
- 类、函数名称,要简单、易理解
- 类与类、模块与模块的关系要尽量简单,保持线性原则,模块分层合理
一般来说,只要你的代码易读易理解,可维护性不会差到哪里去。拥有结构化思维,代码的流程肯定不会乱。
可维护性还有很多要考虑的,就比如我们大部分组件内部也需要考虑扩展性、复用性。把可变的部分设计成抽象类或者接口暴露给上层,由业务注入变化/扩展的实体;把一些不变的部分抽成工具类或者基类,通过静态或者组合来调用,减少内部依赖。
更深的,那就需要熟练掌握设计原则、设计思想、编程范式、架构思维,利用你的重构意识、抽象意识、封装意识、甚至"洁癖"意识,创造优秀的代码。
5.稳定
稳定性,包括性能以及质量。代码稳定,是衡量组件的一个重要指标,是一个状态。
组件不稳定,内部实现脏、乱、差,会体现到应用软件的BUG上。
设计要素不是独立的,而是有些有关联的。比如组件易维护,组件稳定性一般不会差到哪去。提升了稳定性,代码的可用性也会提升;
总之,做好模块化解耦的组件,能提高团队开发效率,后续有精力往更深的技术方向研究、以及追求极致的用户体验。