在公司主要写些模仿硬件行为的c仿真代码,即cmodel。

扁平的struct没什么不好的

对于一个单一的功能模块来说,直接采用扁平的功能模块,与把整个模块(尽力)拆解成互相解耦的子模块、每个子模块单独赋予config的struct相比,恐怕是有一定优势的。

struct {
  int a;
  int b;
}cfg_a;
struct {
  int c;
  int d;
}cfg_b;
struct {
  cfg_a a_cfg;
  cfg_b b_cfg;
}cfg_all;

// vs

struct {
  int a;
  int b;
  int c;
  int d;
}cfg_all_flat;

第二种方法,唯一的缺点应该是“可能”违背DRY原则,即如果有多个子模块的话,可能会需要重复编码。但是实际上,在cmodel这个范畴内,这并不会有什么问题,因为从一开始模块的数量就是确定的。

另外,在编码的时候,所有子函数用同一个cfg_all_flat不会有什么问题,除了代码的自我解释能力可能不太足(其实可以通过struct声明时的注释稍微解决);在效率上,直接传结构体指针并不会对效率有影响——即使是传值,那么一个最多几百个int的结构体,也不会对效率构成威胁;可维护性上,一个拍平的struct应该是更容易维护的,因为值的添加或删除只需要在声明和确实使用的地方改,这个另一种层级型的差不多。
但是拍平的struct有一个很大的优势,可以很方便地写print方法,直接用一个宏定义差不多就行,因为这种拍平的写法,其变量名的确是能自我解释的。

#define print(value) do{printf(#value " = %d\n", value);}while(0)
print(cfg.a);

重构的两种选项:小步快跑和从头再来

  • 小步快跑:小型重构,让代码时刻保持在能编译能运行的状态,一步一步替换其中需要重构的代码,在最后实现完全重构。这种方法类似忒修斯之船,木板替换完了,船变成了崭新的。
  • 从头再来:大型重构,不如重写。