C语言实现OOP——轻量级的面向对象 C 语言编程框架 LW_OOPC 介绍(三)

文章目录

C 语言编程框架 LW_OOPC 介绍(三)

方案的可扩展性如何?

假设我们希望添加一种Ternary_node类型来表示三元操作符,如?:(也就是if-then-else操作符),看看,难度有多大?

​ 事实上,正是因为前面的设计是面向对象的,要增加一种节点类型易如反掌:

// 三元表达式节点
CLASS(Ternary_node)
{
  EXTENDS(Expr_node); // 继承Expr_node

  char op[3];         // 假设操作符最长不超过2个字符
  Expr* left;
  Expr* middle;
  Expr* right;

  // 初始化三元表达式节点(传入一个操作符和三个子表达式)
  void (*init)(Ternary_node* t, const char* op, Expr* left, Expr* middle, Expr* right);
};

在Expr中添加创建三元表达式的方法:

// 表达式(子树的概念),其中,init*方法族提供了构建子树的高层API,方便用户使用
CLASS(Expr)
{

  int use;     // 引用计数

  Expr_node* p;    // 子树的根节点

  //……       // 既有实现

  // 构建三元表达式(包含一个操作符,三个子表达式)

  void (*initTernary)(Expr* t, const char*, Expr*, Expr*, Expr*);

  // 构建三元表达式的重载形式(通过传入一个整型值参数,构造三个子表达式均为整数表达式的三元表达式)

  void (*initTernaryX)(Expr* t, const char*, int, int, int);

  //……        // 既有实现
};

请读者参照Binary_node的现有实现,实现出Ternary_node,这里不再赘述。一旦实现出Ternary_node,我们就可以这样创建表达式树并打印:

 // …… // 创建expr1、expr2、expr3、expr对象(指针)

  expr1->initUnaryX(expr1, "-", 0);
  expr2->initUnaryX(expr2, "-", 5);
  expr3->initBinaryX(expr3, "+", 3, 4);
  expr->initTernary(expr, "?:", expr1, expr2, expr3);
  expr->print(expr);
  printf("\n");

为了支持新的节点类型,对原有代码的更动很少(仅对Expr类有增加方法),而且只有新增操作(新增类,新增方法),但没有修改操作(指修改原有方法),面向对象的设计赋予了系统极大的弹性,让程序在应对变化时,更加从容。在这个例子中,LW_OOPC帮助我们在C语言的世界里营造出OO的天地,带领我们再一次领略了面向对象的风采。

LW_OOPC最佳实践

​ 说得简单一点,要想使用好LW_OOPC这套宏,还得首先懂面向对象,要遵循面向对象设计的那些大原则,比如开闭原则等。在C语言中使用面向对象,根据实际使用的情况,给出如下建议:

1) 继承层次不宜过深,建议最多三层(接口、抽象类、具体类,参见图 1和图 2)
继承层次过深,在Java/C#/C++中均不推崇,在C语言中实践面向对象的时候,尤其要遵循这一点,只有这样,代码才能简单清爽。

2) 尽量避免多重继承
尽可能使用单线继承,但可实现多个接口(与Java中的单根继承类似)。

3) 尽量避免具体类继承具体类
具体类继承具体类,不符合抽象的原则,要尽量避免。

4) 各继承层次分别维护好自己的数据
子类尽量不要直接访问祖先类的数据,如果确实需要访问,应当通过祖先类提供的函数,以函数调用的方式间接访问。

图 1
C语言实现OOP——轻量级的面向对象 C 语言编程框架 LW_OOPC 介绍(三)

图 2
C语言实现OOP——轻量级的面向对象 C 语言编程框架 LW_OOPC 介绍(三)

LW_OOPC的优点:

1) 轻量级

2) 广泛的适应性,能够适应各种平台,各种编译器(能支持C的地方,基本上都能支持)

3) 帮助懂OO的Java/C++程序员写出面向对象的C程序。

4) 使用C,也能引入OO的设计思想和方法,在团队的C/C++分歧严重时可能非常有用。

LW_OOPC的缺点:

1) 无法支持重载(C语言不支持所致)

2) 不完全的封装(无法区分私有、保护和公有)
LW_OOPC的INTERFACE/ABS_CLASS/CLASS三个宏展开后都是C语言的struct,其成员全是公有的,宏本身并无能力提供良好地封装层次的支持,所以,只能从编程规范和编程风格上进行引导。

3) 不支持RTTI

既然不支持RTTI,那么显然也无法支持安全的向下转型(C++中的dynamic_cast的转型功能)

4) 不支持拷贝构造以及赋值语义

5) 转换成接口的表述有点麻烦,表达形式相比C++要啰嗦很多。

6) 有学习成本,需要用户学习并习惯这套宏

前四条缺点,实质上并非是LW_OOPC的缺点,而是C相对C++而言的缺点,在这里,之所以也一并列上,是希望用户不要对LW_OOPC抱太高的期望,毕竟它也只是一套C语言的宏而已,C语言有的缺点,LW_OOPC并不能够解决。

总结:

尽管如此,在使用C语言编程的时候,在某些情形下,你可能想要通过面向对象来更好的组织代码。偶尔,你也想要用用某个设计模式,此时,这套宏能够帮得上忙,使用它,有助于写出相对易于理解和维护的面向对象的代码。因篇幅所限,本文中没有介绍LW_OOPC的高级特性,譬如对内存泄漏检测的支持。示例的完整代码、LW_OOPC的最新版本以及关于这套宏的更加详细的使用指南,请读者访问http://lwoopc.sourceforge.net获取。最后,期望有兴趣的读者,发挥聪明才智,提出改进建议,让LW_OOPC变得越来越好!

幕后花絮:

在完善LW_OOPC宏的过程中,我也认真研究了参考资料中列出的材料。最初V1.0版本有将近25个宏,后来,收到一些同事的反馈,认为少量宏晦涩难记,而且不易理解,经过认真考虑,删除掉5个宏,形成现在的20个宏。相比其他用C实现面向对象的方案,LW_OOPC简洁优雅,每个宏的命名都经过仔细地推敲,尽可能做到简单易懂,便于记忆。

但愿LW_OOPC真的能够帮助到奋斗在一线的C程序员们。

参考资料:

[1] 高焕堂。UML+OOPC嵌入式C语言开发精讲,电子工业出版社,2008 年9月。

[2] Object-oriented Programming with ANSI-C,下载地址:http://www.planetpdf.com/codecuts/pdfs/ooc.pdf。

[3] C实现面向对象的方法,http://www.eventhelix.com/RealtimeMantra/basics/object_oriented_programming_in_c.htm

上一篇:前端开发框架jQuery的优势与基础知识分享


下一篇:算法分析——括号匹配