最近出差到一家公司做resource(悲催啊...), 接手一个关于两个设备连接功能的开发。
代码量大于在10K左右,使用C语言开发。这家公司的软件结构与我之前接触有一些区别。
由于很少有机会大规模开发,所以在这里总结一下开发感悟:
1、基础架构:
目前这家公司的base没有提供基本的数据结构操作,如链表,队列等。导致一些复杂数据结构使用数组,
在对这些数据结构操作的时候,函数略显难看,臃肿。进而导致一些设计上,也不能灵活展开。
感悟:应该实现最基本的通用链表。然后基于此实现更复杂的数据结构。如果项目要求不能动态申请内存,
也可以使用固定数组来实现链表。(后续希望能在GitHub 实现一个通用链表实现
线性和非线性,参考linux内核),作为以后自己的脚手架)、
2、系统接口的使用
吐糟一下,目前这个公司的人员在编程能力上一般,不使用链表等数据结构,系统调用也不太敢用。
当然这些对开发并没有多少影响。但是不使用线程,会限制一些设计实现。还好目前没有需要使用一些诸如
生产者-消费者”等设计模型的需求。
3、多线程同步
之前项目的结构层次比较深,大概有7~8层调用结构。而下层通知上层事件,使用注册回调函数的方式实现。
这种方式在处理线程同步时极其麻烦。
a、下层在lock的情况下回调通知。这就要求在上层的回调处理函数中不能再调用下层的接口,否则
会引起锁的递归调用。另外从模块角度来看,回调通知属于离开本模块的调用,理应unlock。
b、下层在unlock的情况下回调通知。往往有一些情况,需要先进行一些本层处理,然后回调通知,
最后再进行做一些本层处理。在unlock回调通知结束以后,重新lock。这个时候本层的状态
(context《上下文》)可能与之前本层处理的状态不一致了,需要重新取回本层的状态。
但是往往这种函数在语义上,认为回调通知前后状态是一致的。所以很多时候,
在考虑到代码复杂度后,选择妥协,重新lock后,认为与之前状态一致。这样做就有可能滋生一些极端情况
的诡异问题。
目前这个公司我负责模块的层次比较简单。另外与其他大模块间通信使用IPC。所以使用线程同步
会简单很多。、
感悟:如果在一个进程内实现深层次的软件结构,可以考虑大模块之前使用消息队列等方式通信。
这样在需要回调通知的使用,发送消息到消息队列就可以了,不用考虑回调函数unlock问题。
4、业务复杂度
目前实现的这个功能从业务逻辑上来看,是比较复杂的。由于没有提供很好的数据结构操作支持,
所以实现起来,隐约感觉到C语言的限制。以后在遇到一些复杂需求的时候,应该考虑是不是可以使用
更高级的语言来实现,也许会简单一些。哎,想尝试一下JAVA的项目。由于没有使用过高级语言,
所以这也有可能是一厢情愿。
5、如何堆代码
在大规模写代码的时候,应该划分一些层次,尽可能的按照OO的思想,把功能设计成一些小模块。
小模块有对应的数据结构很一些关联函数,按照如下方式组织:
typedef struct _module{
}module;
module mod;
void fun1(moduld *mod, int a);
void fun2(module *mod, int b):
感悟:之前的一些代码由于时间紧迫,胡乱的堆起来,现在看看,真的是非常难看。