别了,中央集权!- -| 回首页 | 2005年索引 | - -库设计就是语言设计,语言设计就是库设计

Let's 规范编码- -

                                      

陈旧的话题,崭新的感受。

"没有规矩,不成方圆",地球人都知道规范的重要性;地球上的软件程序员都知道代码规范的意义。但并不严格遵守规范进行编程的SE却并不少见——包括本人,我一直知道规范的编码对于代码的可读性和可维护性是非常重要的。但由于在写代码时,占据绝大部分大脑意识的是功能的实现,而所谓规范只不过是一个附属品而已。

最近的一个项目让我彻底的认识到了规范的重要意义——

一段时间之前,老板交给我一个任务,要求我写一个工具,在底层的C/C++代码插入DEBUG信息,以便调试时使用。

DEBUG信息包括两部分:

1)被调用的函数名以及相关的参数值;
2)相关的Message名字以及Message Content。

第一个很好办,写一个C/C++的语法分析器,找到函数入口和出口的位置插入enter和leave此函数的LOG信息,然后根据函数各个参数的类型,在入口处插入各个参数值的LOG信息就行了。这是一个General way,除了需要化点时间之外,没什么特别的。

但第二个问题却根本不存在一个必然的方法。为了理解这一点,先看看我们的平台对Message的处理方式。

由于平台的OS是Message-Drived的,所以Application的处理方式是这样的:

int main(int argc, char** argv)
{
   void* msg;
   MSGID id;

   while(1){
       id = GetMessage(&msg);
       if(id != INVALID_MSG){
          HandleMessage(id, msg);
       }
   }
}

//这里定义Message ID
#define MSG_KEY_DOWN     0x0001
#define MSG_DB_MESSAGE   0x0002

//这里定义Message 相关的结构
typedef struct
{
   KEYID keyid;
}
KeyDownMsgStruct;

typdef struct
{
   RID routeid;
   char data[MSG_SIZE];
}
DBMessageMsgStruct;

//具体的Message处理函数
extern void HandleKeyDownMsg(KeyDownMsgStruct*);
extern void HandleDBMessageMsg(DBMessageMsgStruct*);

//Message调试函数
extern void DebugMessage(MSGID, void*);

//Message处理函数,所有具体的Message函数和Message调试函数都在
//此函数里被调用。
void HandleMessage(MSGID id, void* msg)
{
    //DebugMessage正是为了这个工具而插入的函数;
    //这个函数要靠工具自动生成。
    DebugMessage(id, msg);
    switch(id){
    case MSG_KEY_DOWN:
          HandleKeyDownMsg((KeyDownMsgStruct*)msg);
          break;
    case MSG_DB_MESSAGE:
          HandleDBMessageMsg((DBMessageMsgStruct*)msg);
          break;
    ......
    }
    return;
}

通过这断代码,我们可以了解到这个目标平台上的一个Application总是处于

1)从消息队列读取消息(GetMessage);
2)处理消息(HandleMessage)

的循环当中。

而系统中每一个Message都具备:
1)Message ID
2)Message Content

通过函数GetMessage可以得到它们。

而我的任务是,编写一个工具自动生成DebugMessage函数。它们内容应该如下:

void HandleMessage(MSGID id, void* msg)
{
    switch(id){
    case MSG_KEY_DOWN:
    {
          DEBUG("Message ID: MSG_KEY_DOWN");
          KeyDownMsgStruct* kdm = (KeyDownMsgStruct*)msg;
          DEBUG("KeyID:(%d)", kdm->keyid);
          break;
    }
    case MSG_DB_MESSAGE:
    {
          DEBUG("Message ID: MSG_DB_MESSAGE");
          DBMessageMsgStruct* dbm = (DBMessageMsgStruct*)msg;
          DEBUG("routeid:(%d)", dbm->routeid);
          DEBUG("data:(%s)", dbm->data);
          break;   
    }
    ......
    }
}

很容易看出,自动生成这个函数的关键在于工具能够知道Message ID和Message Structure(Content)之间的对应关系。

最初我认为这个问题根本不可能用自动的方法实现。等我把函数的Debug完成之后,我化了1分钟时间考虑了一下,除非从命名上可以找到两者之间的必然关联,否则,我只能手工的写这个函数。

我对此并没有报太大的希望,因为我知道:

1)底层代码的设计者并不知道我们会有这种需求,所以并无必要通过名称建立这种关联;
2)即使命名有此关联,但由于我们的系统有近千种Message,要求程序员做到一丝不苟的遵守规范,按照我个人的经验,除非编写者有很强的规范意识,否则很容易出现偏差。

但我仍然带着一丝侥幸的心理去察看了一个相关代码。刚看到第一个case,我的心跳就开始加快,随着我一路看下去,到了最后一个message时,我的心几乎要跳了出来!!

代码的设计者和编写者就像一个巫师已经预见到我们未来要实现这个工具一样,不仅通过命名来建立起每一个Message ID和Message Structure之间的关联,并且近千个Message无一例外。

我当时只有一种冲动——想给这些代码的设计者和编写者一个热烈的拥抱。

通过这件事情,我的代码观发生了一次革命,意识到了规范的代码能够带来多么长远的意义。我把它写出来与诸君分享,希望你能够和我一样,能够从中得到一些有益的启示。

- 作者: 上帝没发笑 2005年01月31日, 星期一 04:14 加入博采

Trackback

你可以使用这个链接引用该篇文章 http://publishblog.blogchina.com/blog/tb.b?diaryID=650117

回复

评论内容: