基础复杂性是守恒的,需要解决的基本问题始终是需要解决的;最终的复杂性,却取决于基础复杂性彼此间的依赖,如果互相依赖,最终将得到指数级的复杂性,而如果彼此独立,最终只是若干基础复杂性简单的累加,因此,程序需要更好的的Design和Organize,主要任务就是“降低模块间的依赖至最小”,而对开发效率有重大影响的,主要是编译期依赖,为此,人们发明了若干技术来降低编译期依赖
1,模块系统
C++继承了C的include机制,缺少ABI,缺少模块系统,客户端代码依赖于对象的内存布局,任何对头文件(类定义)的修改都导致需要编译所有客户端;传统上只能如下解决:
部分基于C++的平台提供了初步的模块系统来改善这个问题,如COM组件的dll;但依然基于内存模型,只能新增接口,不能修改原来的接口和删除原来的接口
改良include:使用Pimpl惯用法;使用前置声明;剔除不必要的include;预编译等(后三项主要是为了编译速度)
Java有一个ABI,基于ABI有一个模块系统,至少客户代码不再依赖于内存布局,而仅仅依赖于函数签名;类库作者可以较为放心的添加新功能,而不会影响那些不受控的遍及世界各地的客户代码
但模块系统依然有一个不能解决的问题:编译时依然需要“引用”的类和接口的定义
2,非侵入性
编译时对“引用”的类和接口定义的依赖,我们称之为“侵入性”的;任何显式的“接口”、“基类”都是侵入性的,不可避免的带来编译期依赖;即使这些依赖很小,但依然有办法而且应该尽可能消除
Java的法宝是反射,但效率低,并不具有类型安全性,因此,除非独立性和灵活性需求大于效率和类型安全需求的场合,一般不要使用反射;
而在非侵入性问题上,C++有强大的语言机制--模板:类型安全并且效率无损;它不需要你做任何继承操作,只需要满足模板参数的概念约束,提供“语法兼容”的调用即可;注意是“语法兼容”即可,这意味着你的函数可以是“static”的,也可以是“virtual”的,也可以什么都不是,只要签名一致就可以; 比如在扩充STL时,你不需要include任何STL头文件
当然模块系统和非侵入性在解决依赖问题上是正交的;作为降低编译期依赖的有效机制,以模板参数的概念约束形成的模块接口,应得到更多应用