数据库存储了几乎所有的业务数据,而这些数据毕竟不是静态的,那么就必然需要我们来写程序完成数据的加工和整合,数据库仅仅保存我们加工整合后的结果,因此必然的,我们需要把数据库中的数据读到程序中,然后加工后再存入数据库,同样必然的,我们需要在程序中维持数据库表的结构,那么很显然,我们需要在程序中定义很多结构体或者联合体数组来保存数据库查询的结果,每个结构体代表了一个纪录,很多纪录就用结构体数组来表示,如此一来,程序就会显得很臃肿,比如会出现下列的结构体:
struct girl {
char *name
int age;
int sexy;
int height;
int weight;
...
}
结构体的大小完全取决于数据库表的列。这样虽然可行但不甚合理,如果我们换一种思路或许能让事情更简单,读了OpenSSL的d2i和i2d的代码,发现人家就没有采用这样臃肿的方式设计,虽然OpenSSL没有涉及数据库但是毕竟涉及到了格式化的文件,比如pem/der文件,我们当前讨论的意义上,这些格式化文件和数据库是一致的,OpenSSL的方式十分值得我们来思考。
我们可以不考虑数据库表的具体性质,仅仅将表的一列作为一个“名称-值”对,这样每一条纪录就可以被看作这样的“对”的数组,数组的大小就是列的数量,如上例的一条纪录{"LiLi",21,1000000...,175,100,...},我们可以以下这样定义{{name,"LiLi"},{age,21},{sexy,1000000...},{height,175},{weight,100},...},为了统一化,我们可以将诸如{name,"LiLi"}的项设计成一个结构体,就是“名称-值”的结构体:
struct abc {
char *name;
char *value;
}
我们看看这样是否合理,又没有改进的空间,...很合理,但是虽然从数据库中读出的结果都是字符串char*类型,但我们的程序需要将之转化为其合理的数据类型才能处理,也就是说value最终并不全是字符串类型,可能是任意类型,name显然就是列名了。既然value可以是任意类型,我们便需要将之定义成联合体:
struct abc {
char *name;
union {
char *db_char; //从数据库中读取的原始数据
int int_value; //如果是int型,则int_value为转为int型的数据,下同...
...
}value;
}
接下来我们看一下还有没有改进的空间,看看name字段,它仅仅表示列名,它真的必不可少吗?不!其实它仅仅起到一个索引的作用,帮助程序找到特定的列,既然我们准备将结构体abc按照纪录列设计成数组,那么数组的下标就能完成此任,再设计一个name字段就是冗余了,所以我们决定砍掉它:
struct abc {
union {
char *db_char; //从数据库中读取的原始数据
int int_value; //如果是int型,则int_value为转为int型的数据,下同...
...
}value;
}
那么数组下标如何索引数据库纪录列,毕竟它仅仅是一些毫无意义的数字,为了使程序有更强的可读性和可维护性,我们需要赋予无意义的数字以意义,于是想到了枚举,于是定义下列枚举:
enum {
GIRL_NAME = 0,
GIRL_AGE,
GIRL_SEXY,
GIRL_HEIGHT,
GIRL_WEIGHT,
...
TOTAL
}
于是我们只需要为每一条数据库纪录分配TOTAL个struct abc即可,然后直接用枚举类型来索引数组特定的元素。最终枚举和联合帮了大忙,枚举赋予了数字以意义,而联合则使得数据定义可以统一,今后写程序时可以借鉴一下这种方式,同时也感慨一下C语言真的很强大。