一个软件架构师的实践回顾(续)

花了两个周末,把原先仅支持Windows和Linux的嵌入式应用平台成功地跑在了一块Cortex-M4开发板上,这距开始着手设计这个架构刚好三年。每每与同行聊起此事,他们要问的第一个问题必然是:“怎么不用安卓?” 答曰:“养不起那么大的Team。”
安卓大概也就是给创业公司拿去做做DEMO找找投资,或者给山寨公司套套壳比较合适,不然,一不小心就会把几十号人陷进去,尤其是在你需要的功能集远远小于它所提供的功能集的情形下。今天更是倍感庆幸,如果当初选择安卓,要转到M4上只能完全推倒重来。
总之,同定制安卓比,打造一个精简的嵌入式应用平台没有想象的那么复杂。

设计初期,语言的选择是C++,平台是Windows。C++远不是最佳的编程语言,但却是妥协性最好的编程语言,如何使用它完全取决于架构师,而非语言标准及其支持库本身;至于选择Windows,则是因为芯片选型尚未完成,而刚好手里面有一套基于Windows的操作系统抽象库。那么,第一个任务就是构建一个好用的操作系统抽象层——这里面的重点不是抽象,而是好用。因此,光有线程封装是不够的,还得有一个更高级的事件抽象模型,于是借鉴了不少Java的东西,主要是JMS和并发包里的线程池(我从来不耻于承认Java是自己最喜欢的语言),当然需要做许多精简,使其更适合嵌入式终端的需求。等到这个库差不多稳定下来的时候,硬件设计也刚好出炉,这时再把线程封装换成Posix,就可以直接拿到开发板上跑了。

分层和模块化是我踏入这个行业所学到的第一课,还记得多年前浩哥拿着飞利浦的PPT给大家讲,那时听得稀里糊涂,现在这些思想却几乎已经成了一种思维本能。简单地说,分层有利于降低软件的物理依赖性,不仅适合一个产品系列的并行开发,也可以在很大程度上降低频繁的产品需求变更对软件开发的冲击。比如,他们问:“LED给用户的感觉太生硬了,听说最新的驱动支持可以调节的亮度渐变,给你两周的时间能加上去吗?”
这时你可以和他们要过最新的UX Spec,然后内心镇定却故作忐忑状地答道:“两周可能比较紧张——尽力吧!”

第二个核心模块是设备管理,这点做得有些过。:
我崇拜Linus,但同时又是宏内核的反对者,一直倾向于将设备操作从内核搬到用户层,动机嘛,其实不复杂,就是希望降低对芯片商的技术依赖。当年在S社的时候,内核驱动的bug都会在芯片商那边拖上十天半月,至于小客户,那只好听天由命。
后来发现设备管理器显然是被过度设计了,大家都略有怨言,刚好来了个小印,做内核的,捣鼓了个ko,又把用户层的操作全给搬了回去。挖剩下的那些,也就只能给缺板子的同学们在Ubuntu上调着玩。

然后是控制和状态推送模块,简而言之就是MVC加一个protocol buffer,这个模块可以让一个通过蓝牙或无线或任意其它什么方式连接到设备上的远程应用便捷而实时地对设备进行操作和监控。这也是仅有的一个使用了大量脚本自动生成的代码的模块,其时曾逆想某天人工智能学会编码的时候这碗饭自己还吃成吃不成。

最后一个模块是一个多级有限状态机。
概念是同事提出来的,但因为当时我们已经有一个可以做演示的原型,我并不是十分热心,总是觉得必要性不大。但幸亏被成功说服,不然后期频繁的UX需求变更怎么都能把你的架构戳成危楼一座。也正是因为有状态机的支持,许多找不到根本原因的许多问题也都放到了应用层做workround,不就是加一个迁移或者顶多一个状态吗,几个表格的变动。
一个设计良好的状态机可以消灭大部分if/else和switch/case,毕竟,人类的大脑在读图表的时候犯错的概率还是低好多——何况改图表不是那么容易引入看不到的副作用。

开发初期同学们还是有些抵触情绪,抱怨最多的是过度设计,因此加班加点地做过还几次重构,最终我自己认为还是达到了务实的标准。还是那句老话:软件不是设计出来的,是演进出来的,凭空设计的软件、没有进化能力的软件,都是死的软件。另一个担心是觉得这个平台还是有点重,如果真的有一天要拿到256K ROM,128K RAM的微控制器上怕跑不起来,这也是早期大家反对使用C++的原因之一,我当时的应对方案是限制标准模板库的使用,至少不能让它们出现在函数的参数表中。基础模块的开发还是维系了这一原则,但到应用就有点乱,string、map漫天飞——赶进度嘛!好在最上层应用的重用也没有太大意义,该重用的也都重用了,这也进一步佐证了软件分层的意义。

此番回顾算是给三年前另一篇文章一个交代,实际上,以上每一个话题都值得花些时间深入探讨。

anyShare分享到:

Meta

cli Written by:

Be First to Comment

Leave a Reply