---DO NOT REPEAT YOURSELF---
建设性的懒惰,是程序员的第一美德。最简单的懒惰就是不要重复自己,简单到一个命名规则,例如C语言中:
int osip_list_init (osip_list_t *li)
void osip_list_special_free (osip_list_t *li, void (free_func)(void *))
void osip_list_ofchar_free (osip_list_t *li)
int osip_list_size (const osip_list_t *li)
int osip_list_eol (const osip_list_t *li, int pos)
int osip_list_add (osip_list_t *li, void *element, int pos)
void * osip_list_get (const osip_list_t *li, int pos)
int osip_list_remove (osip_list_t *li, int pos)
这是一个原型,但它重复的太厉害。osip是软件包名,它应该是一个范围声明,说明所有代码都是它的一部分。list是一个模块名,事实上它也是首参数的类型名,它应该只保留一个。这事实上非常接近于Oberon的模块定义. 加上类型缩写,即与C++的定义完全一致。
C语言的派生语言基本都采用这个形式。
namespace osip {
class list_t {
int init ()
void special_free (void (free_func)(void *))
void ofchar_free ()
int size () const
int eol (const int pos)
int add (void *element, int pos)
void * get (const int pos)
int remove (int pos)
}
}
---CLOSE TO MODIFY, OPEN TO EXTEND---
C语言缺少复杂数据结构,以及数据隐藏的能力. 良好的API设计不得不使用Opaque data这样的数据结构来表示复杂抽象, 隐藏可能的模型变化。C语言中的容器类型事实上只有数组,如果是迭代结构,例如map,hash,jumplist,只能由多个API(begin,next,end)来表示。
C++比C进一步,但头文件的纯文本方式公开的模型内部结构,但由于静态编译,类型已经在编译时在程序中确定,相对要好一点。
这一点上脚本语言做得更好,最常用的复杂数据结构被内置于语言,甚至做为语言的基本结构。如lua的Table,它即是数组,也是映射表,还是对象结构。
更重要的是,这些复杂数据结构,方便领域模型到代码的映射,使代码表达领域模型更加直观。而在C语言中,我们需要最大限度的简化领域模型,过多个层次和模型包裹只会导致混乱的代码。最明显,也许不被大家知晓的例子是文件IO,在C语言层次只提供了FILE这一个整体抽象,它的内部结构不公开。而C++中提供ostream/istream,filebuf这三个对象的一个相对复杂结构。不论模型的好坏,C++的模型如果用C来实现,肯定是一场灾难。
一句话来说,由于C语言的基础设施比较少,由这些基础设施实现的代码有较高的耦合性,因此几乎马上就到了必须以内聚性接口来封装的地步。而面向对象的语言如C++等,由于有类的支持,低层代码耦合性被控制的比较好。因此,可以设计相对复杂模型。