LION的技术博客

纸上得来终觉浅,绝知此事要躬行


Project maintained by lionloveqin Hosted on GitHub Pages — Theme of midnight

对Team最新开发的框架的吐槽

项目背景

Team目前的解决方案是基于QNX的,由于种种原因,决定逐渐切换到Linux平台,而这似乎也是业界的一个趋势,由BMW主推,详见Genivi项目。

切换到QNX对我们一线工程师可以说是一个大利好啊,以前QNX的解决方案只能够有Binary提供,没有源码,学习到的技术有限,并且出了问题还不一定能即时响应。而这一切在Linux平台上都可以改变,我们可以在各种开源的框架(Tracker/LMS/SQLite/GStreamer...)上整合出自己的解决方案,出问题了可以自己阅读源码解决。虽然没有了商业上的支持,意味着出了问题会面临更大的挑战,但是工程师的本性就是拥抱挑战,不是吗? 还有作为一名C++工程师,对新标准和新技术的使用渴望当然也是无比强烈的。目前在公司的Linux平台的交叉编译器的版本是g++ 4.7.3, 这就意味着我可以放心的使用std::shared_ptr了,终于不用自己造这样的轮子了。还有我们的目标平台还支持Boost 1.51.0,泪流满面啊,这意味着以前对部分Boost组件的学习经验也可以派上用场了。

感谢老板的信任,可以让我从一开始就能加入到这样一个全新的项目的开发中。我在中间承担了Tracker/Database等相关模块的开发,而框架设计的工作主要由另外的同事负责。在使用中我能比较明显的感觉到框架中的一些不合理,这些让我觉得很难受,和组内另一个同事聊的时候,他说遇到蛋疼的代码,请无视,可是我想说“臣妾做不到啊”。

具体吐槽

下面我客观的评价一下其中我觉得不好的地方(和个人思想有关,如果有异议,请无视),也希望我自己在我自己项目中能够避免这些问题。

1. 命名风格简直是天花乱坠。

比如 源文件/头文件的命名有foobarzoo.cpp/FooBarZoo.cpp/fbz.cpp;成员变量的名字有m_spExample/mspExample/example;成员函数或者全局函数的命名也是风格各异;类的名字也有各种风格CExample/Example...

2. 友元类的使用。

我不否认合理的使用友元类能够带来设计和代码的简洁,但是过多的使用是不是意味着我们的设计出了一些问题,代码里面有坏的味道了。

3. COM风格的接口设计

不可否认老一批Windows程序员或多或少都接触过COM思想/组件, 我也向周围的几个同事(使用过Direct Show)咨询了一下他们对COM组件的理解,但是基本都没有说出COM真正的精髓的(虽然我也不清楚,但是他没又说说到我的痛点)。而作为年轻一代的Linux程序员,说实话不是愿接受那样的编程风格。 在我们设计里面,为了实现接口查询(QueryInterface)和对象的引用计数,我们的每个类都必须继承IUnknown这样的接口。

   class IUnknown
   {
   public: 
      virtual HRESULT QueryInterface(HBIID iid, void ** ppObject) = 0;
      virtual UInt32 AddRef(void) = 0; 
      virtual UInt32 Release(void) = 0;
   };

可是COM实现这样的接口是为了跨语言实现组件之间的通信啊,而我们的代码最终就只有一个进程,需要QueryInterface干嘛啊。那好,不是还有引用计数吗?而这个不是用std::shared_ptr就可以完美解决了吗。其实最终的类不仅需要继承IUnknown,还必须继承CUnKnown,这个CUnKnown实现了reference count, 然后通过IUnknown中的AddRef/Release来调用。一个类既需要实现IUnknown也需要继承CUnKnown,我认为这是一个相当糟糕的设计(不是说COM,而是对于我们的使用场景),假如哪个类继承了CUnKnown和忘记了IUnknown呢?因为现在的实现里面有个trick是在CUnKnown里面保存了一个IUnknown的指针,在CUnKnown中通过reinterpret_cast来cast this指针到IUnknown*,可是我们没有继承IUnknown啊,天知道运行时会发生什么。 我认为这样的设计是对COM的错误认识照成的,我们为什么不用一个Factory函数来提供类的实例呢?std::shared_ptr T::findInstance()。 为什么不能让CUnKnown直接继承IUnknown呢?说是为了解决菱形继承的问题。我想说,首先,我认为出现菱形继承本来就是一个不好设计,其次,这管菱形继承什么事啊,多份refcount?

4. 有一个函数的参数有10+的参数,而且都是Boilerplate Code

相信读过Real World Haskell(正在啃,真心难啊)的都知道里面对Boilerplate Code的看法吧。

5. 重复造低质量的轮子,比如对Mutex等的封装。

那我们至少也得用RAII来封装啊,手动unlock怎么行呢。

6. 代码的组织还是比较糟糕,过多不必要的抽象

层次太深,目录名字不能准确应模块的功能... 那个叫什么redis的不就一层目录吗?

7. 没有引入现代C++程序设计的思想

上面提到的RAII管理资源是一个,还有大量使用char*而不是std::string,使用raw array而不是部分STL组件等。 在这一点上我比较欣然的陈硕的工程能力和思想,这也是我学习的方向。

解决办法

1. 拥抱KISS法则,好的代码是明显没有问题,而不是没有明显的问题

2. Code Review

3. 学习更好的设计,不要闭门造车

comments powered by Disqus