2012年三月月 发布的文章

C语言编码风格和标准

近期在为产品线的知识库编写一些指南类的文档,其中有一项就是对现有的C语言编码规范进行一些修订。为了"有米下锅",我还特意在网上找了一些相关资料。关于C语言编码风格和标准的资料大多都成稿于上个世纪90年代,也就是在C90发布之后的若干年里;在C99发布后,部分资料根据最新的规范做了修订,但也有些资料认为C99对整体风格影响不大,也就保持了原样。

 
在这些资料中,我重点关注了一下这份文档《Recommended C Style and Coding Standards》,它是著名的"Indian Hill C Style and Coding Standards"的更新版,从Google的搜索结果来看,似乎影响很广。这份文档内容不多,言简意赅,特别是后面的几个小节,例如宏、条件编译、可移植性以及ANSI C等章节很值得细致阅读和理解。
 
我试图google该文档的中文版,居然没有找到。也许是这个文档比较老了,或者是其中有些注意事项在当今C编程领域较少能遇到了,再或许就是C语言老了,关注的人少了,总而言之,网上没有该文档的中文版。于是乎我就花了一些时间翻译了一个粗糙的中文版,供那些看E文和我一样吃力的朋友们参考。中文版以Wiki的形式放在了Google code(http://code.google.com/p/recommended-c-style-and-coding-standards-cn/)上了。这里需要先说明的是:翻译过程不是很细致,较随意,有些地方我理解得也不慎透彻,欢迎大家提出自己的见解,后续有时间还会持续地修订。
 
这里提供一个快捷入口^_^:
 
12. 常量
13.
15. 调试
19. Lint
20. Make
22. 结论

Adapter模式的C实现

Adapter(适配器)模式是《Design Pattern》一书中结构类模式集中的第一个模式,也是一个真正被我的同事在产品代码中应用的模式。

Adapter模式也是一个相对容易理解的模式,多数书籍和网络资料在描述这个模式时都使用了一个与电源适配器有关的例子,说不定Adapter模式还真的是源于对电源适配器的再思考和挖掘呢。
 
我们在重构遗留代码时引入了Adapter模式。遗留系统中存在的问题大致是这样的:按照规范,最初的系统只需要支持一种通信协议,这里把这个协议暂叫做proto_a。后来随着规范的丰富,客户又先后引入了两种协议proto_b和proto_c。前辈们在处理这些需求时偷了一个懒儿,选择了直接copy两份一模一样的业务逻辑代码以应对两个新协议,这样在遗留系统中就形成了一份业务逻辑,三份相同的代码的局面。可想而知,这样的代码结构是多么的不易于后续维护啊。一旦业务逻辑发生一处变化,我们就需要修改三个位置。代码味道那是相当的浓烈。在遗留代码的重构过程中,我们决定尝试用Adapter模式解决这个问题。
 
我们的目标是:一套业务逻辑,一套业务代码,支持三种不同的协议。还好这三种协议是基于同源设计的(从同一种协议演化而来),也就是说具备设计统一操作接口的基础,这似乎天生就适合Adapter^_^。
 
我们首先定义多种协议的统一操作接口:
 
/* xx_proto_interface.h */
struct xx_proto_i {
    int (*xx_proto_login)(void *trans);
    int (*xx_proto_logoff)(void *trans);
    int (*xx_proto_req)(void *trans, void *req);
    int (*xx_proto_resp)(void *trans, int *status );
    int (*xx_proto_heartbeat)(void *trans);
};
 
显然Adapter的引入就是因为proto_a、proto_b和proto_c原生的接口与统一接口有差别,无法直接使用。我们以proto_a为例,增加一个proto_a的Adapter层:
 
/* xx_proto_a_adapter.h */
#include "xx_proto_interface.h"
#include "proto_a.h"
 
struct xx_proto_i* get_proto_a_adapter_impl();
 
 
/* xx_proto_a_adapter.c */
#include "xx_proto_a_adapter.h"
 
static int xx_proto_a_login_adapter(void *trans);
static int xx_proto_a_logoff_adapter(void *trans);
static int xx_proto_a_req_adapter(void *trans, void *req);
static int xx_proto_a_resp_adapter(void *trans, int *status );
static int xx_proto_a_heartbeat_adapter(void *trans);
 
struct xx_proto_a_adapter_t {
    struct xx_proto_i impl;
};
 
struct xx_proto_i* get_proto_a_adapter_impl() {
 
    struct xx_proto_a_adapter_t *p = malloc(sizeof(*p));
    if (p == NULL) return NULL;
 
    p->impl.xx_proto_login = xx_proto_a_login_adapter;
    p->impl.xx_proto_logoff = xx_proto_a_logoff_adapter;
    p->impl.xx_proto_req = xx_proto_a_req_adapter;
    p->impl.xx_proto_resp = xx_proto_a_resp_adapter;
    p->impl.xx_proto_heartbeat = xx_proto_a_heartbeat_adapter;
 
    return (struct xx_proto_i*)p;
}
 
static int xx_proto_a_login_adapter(void *trans) {
    printf ("proto_a_login\n");
    /* 这里使用proto_a的原生接口实现login */
    return 0;
}
… …这里省略若干个实现
 
int xx_proto_a_heartbeat_adapter(void *trans) {
    printf ("proto_a_receive_a_heartbeat\n");
    /* 这里使用proto_a的原生接口实现heartbeat */
    return 0;
}
 
实际上我们用proto_a的adapter包装(wrap)了proto_a的原生接口,也就是说proto_a的原生协议实现对客户是不可见的。在《设计模式》书中,Adapter模式的别名也叫Wrapper。
 
客户端是这么来使用协议的,客户端只需要使用统一的xx_proto_i中的接口即可:
 
#include "xx_proto.h"
#include "xx_proto_a_adapter.h"
#include "xx_proto_b_adapter.h"
#include "xx_proto_c_adapter.h"
 
int main() {
    /* 根据需要我们灵活选择使用proto_a、proto_b或proto_c的adapter实现 */
    struct  xx_proto_i *p = get_proto_a_adapter_impl();
    struct proto_a_trans;
    /* some initializations */
    … …
 
    p->xx_proto_login(&trans);
    p->xx_proto_req(&trans, …);
 
    … …
}
 
任何事情都是有代价的,从上面可以看出Adapter模式的引用也使得代码变得庞大,很多proto的Adapter接口的实现可能都会是类似的和浅包装的。但与之前的问题相比,这些代价显然要小很多,并且用宏可以做适当改善。
 
题外话:适度抽象可以使得代码更加清晰,易于改变和维护。但物极必反,过度抽象反倒会让代码的可读性和易维护性下降,特别是在代码中使用了模式等手法时,千万不能沉迷,因为大家在理解你的多层过度抽象上所要付出的代价可能更大。
如发现本站页面被黑,比如:挂载广告、挖矿等恶意代码,请朋友们及时联系我。十分感谢! Go语言第一课 Go语言精进之路1 Go语言精进之路2 商务合作请联系bigwhite.cn AT aliyun.com

欢迎使用邮件订阅我的博客

输入邮箱订阅本站,只要有新文章发布,就会第一时间发送邮件通知你哦!

这里是 Tony Bai的个人Blog,欢迎访问、订阅和留言! 订阅Feed请点击上面图片

如果您觉得这里的文章对您有帮助,请扫描上方二维码进行捐赠 ,加油后的Tony Bai将会为您呈现更多精彩的文章,谢谢!

如果您希望通过微信捐赠,请用微信客户端扫描下方赞赏码:

如果您希望通过比特币或以太币捐赠,可以扫描下方二维码:

比特币:

以太币:

如果您喜欢通过微信浏览本站内容,可以扫描下方二维码,订阅本站官方微信订阅号“iamtonybai”;点击二维码,可直达本人官方微博主页^_^:
本站Powered by Digital Ocean VPS。
选择Digital Ocean VPS主机,即可获得10美元现金充值,可 免费使用两个月哟! 著名主机提供商Linode 10$优惠码:linode10,在 这里注册即可免费获 得。阿里云推荐码: 1WFZ0V立享9折!


View Tony Bai's profile on LinkedIn
DigitalOcean Referral Badge

文章

评论

  • 正在加载...

分类

标签

归档



View My Stats