嵌入式软件的问题分析
1 问题背景
一切为了进度,软件开发的首要目标就是以最快的速度满足客户需求,“快”是第一要素,但是短期指标;可复用性、扩展性等长期指标被忽略,导致后期的维护、功能增减调整都非常困难。一个小的业务需求会牵一发而动全身,一个小的故障修复可能引入更多的问题。整个系统包袱越来越沉重,软件的质量和开发周期越来越不可控。
排除软件开发人员的水平和项目进度的原因,主要影响因素还包括软件架构,和软件缺陷的修复能力。对于量产软件,架构问题是先天性的,后期很难大改,只能前期预防;软件缺陷问题是无法避免的,只能期望快速修复。
2 软件架构问题
2.1 软件架构的特点
1. 承载力
正如一艘船最多能装多少人,从软件方面来说是软件架构能承载多少业务或功能需求,当然,这需要架构师一开始架构系统的时候,就需要有一定的预见性。但也没必要为了极小概率事件增加过多的冗余。
2. 易用性
易用性决定了软件的整体开发效率,好的架构会让团队成员容易上手,子系统容易对接,开发效率高,各模块和子系统的编写只需要关注系统的设计和编码工作,其他模块间通信方面的事情架构可以提供很好的兼容。
3. 扩展性
一个水杯除了用来喝水,也可用来喝酒,适应不同场景,在一定范围内满足不同的需求,是非常有必要的。软件架构也是这样,要新增更多的功能就要具备更高的扩展性。可扩展性的关键就在于新增部分不能影响其他,如果增删导致系统整体使用异常,那么这个架构的可扩展性就很差。
4. 伸缩性
伸缩性就是设计的方案或系统是否可以根据需求适配不同数量的功能或子系统,在我们设计的软件系统中,架构的可伸缩性决定了架构的可适配性,例如,当硬件资源不足时,可以调整配置如flash的空间分配,支持减少一些服务但仍能正常运行。
5. 容错性
软件运行中的异常,如用户的非法操作,或者软件本身的小缺陷导致整个系统无法使用,那这个架构容错性就很差。软件中的一些缺陷无法避免,但是我们应尽量保证这个缺陷的影响范围最小。倘若出现系统无法使用的情况,应该有备份方案,比如自动重启或者自动恢复数据等功能,也应该能够让开发人员及时知道问题的发生,以及问题所在的位置并记录错误信息。
在架构设计中,以上五项基本能力缺一不可,某项能力的突出并不能带动其他项,如果某一项能力比较弱,随着时间的推移,问题会越来越大,甚至系统崩溃。就像木桶原理那样,一个木桶的容量不是取决于最长的那根木板,而是取决于最短的那根。
2.2 如何规划软件架构
2.2.1 必须熟悉业务
软件是为业务服务的,业务才是“目的”,软件系统是为了达成业务系统目标的手段和方法。适应当前的业务需求是基础,充分考虑和预测未来的业务扩展,根据业务的扩展性来设计软件的扩展性。如果可预见未来没有扩展重大新业务的需求,那么相应的软件架构就没有必要采用高扩展的软件架构。比如嵌入式的传感器数据采集小设备,就没有必要把云计算等,业务范围不沾边的技术点放到其中。软件架构必须以服务业务为核心思想,不熟悉当前软件业务、和未来业务的扩展的架构师是很难设计出好的软件架构。
2.2.1 借鉴业内成熟的架构
不照搬,并不意味着不要借鉴。借鉴业内成熟的软、硬件架构是相对稳妥、高效的做法。以业内的架构为基础,根据自身业务的特点,进行适配、裁剪和增加新的功能。熟悉业内常规的、成熟的、最新的软件架构是架构师的一项基本功。但熟悉并不是意味着必须立即在目标系统中实施这些软件架构。
2.2.3 采用设计模块
设计模式(Design pattern)代表了最佳实践,设计模式是软件开发人员在开发过程中对一般问题的解决方案;是一套被反复使用的、多数人知晓的代码设计经验的总结,经过相当长的一段时间的试验和错误总结出来的。
使用设计模式是为了重用代码、让代码更容易被他人理解、保证代码可靠性,合理地运用设计模式可以完美地解决很多问题。每种模式在现实中都有相应的原理来与之对应,每种模式都描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是设计模式能被广泛应用的原因。
用设计模式构建一个新的软件模块时,短期会让人感觉有多此一举的味道;但中长期来看,设计模式能够克服“坏”架构的特征。学习这些模式有助于经验不足的开发人员通过一种简单快捷的方式来学习软件设计。尽管设计模块通常被有经验的面向对象的软件开发人员所采用,但是嵌入式软件C语言也可以借鉴,参考《嵌入式软件的设计模式(上)》,《嵌入式软件的设计模式(下)》。
2.2.4 合理的横向和纵向切分
横向切分 :从硬件、驱动、组件到业务层,软件分层隔离。如数据通信:PHY/MAC/IP/TCP/应用层
纵向切分 :根据业务处理流程的环节纵向切分,不同的环节为不同的模块,不同的业务功能为不同的模块,如socket网络、GNSS卫星定位。
2.2.5 按树形结构组织
按照树形结构的方式组织软件系统,不同的大功能拆分为小功能,文件夹内套文件夹的实现形式,命名上统一,方便按功能快速找到对应的源码。
2.2.6 降低模块之间的耦合度
耦合性是一种软件度量,是指一程序中模块及模块之间信息或参数依赖的程度;内聚性是一个和耦合性相对的概念,一般而言低耦合性代表高内聚性,反之亦然。
2.2.7 降低模块与模块之间通信
一个软件内模块与模块之间的通信,构成了一个内部的通信网,避免内部模块的通信采用网状结构,这种解决方案是设计模式中的中介者模式。
2.3 重构和演进架构
架构不能一成不变,要随着业务需求的演进而升级重构,一成不变的架构是危险的,总有一天架构成为业务演进的最严重的制约因素。
这种需要实际开发中除完成既定的项目外,预留部分人力进行架构升级维护,持续小改动,不定期根据业务的需求进行架构的重构,未雨绸缪。 |