C语言的面向对象式重构
从Martin Fowler最早提出重构的概念开始,到现在已经有很长时间了,重构已经是深入忍心了。与其说它是一种方法,不如说是一种思想、一种习惯。本文主要介绍C语言的面向对象式重构,希望对你有帮助,一起来看。
重构(Refactoring)就是在不改变软件现有功能的基础上,通过调整程序代码改善软件的质量、性能,使其程序的设计模式和架构更趋合理,提高软件的扩展性和维护性。
在越来越多的方便实用的语言曾不出穷的今天,c语言作为编程语言的常青树,在用户数方面仍然占据着老大的地位。当然,这与C语言的使用环境有很大关系,诸多的嵌入式和底层代码仍然离不开这个经典的语言。
最近工作有了个空,如何使自己写的C代码容易阅读,便于维护,成为近期我思考的主要问题。
于是,我找到了Martin Fowler写的《重构》。这本书从Martin的经验出发,结合代码中出现的各种“臭味”,提出了重构的思路、指导思想和重构步骤,从而使得代码不断的得到进化,从而使得代码越来越优美,越来越适应于变化,越来越便于维护。这本书虽然是针对Java语言写的,但是大量的重构思路都是语言无关的,我们在任何语言中都可能遇到Martin大师描述的问题,并能够利用Martin的解决思路来改正问题。现在,就把我看这本书的一些想法、思路记录下来,也算是一个读书笔记吧。
一、函数的改进
过程化语言直接面对的是要解决问题的系统责任,也就是系统的功能域。要实现什么样的功能,采用什么样的流程来解决,这些内容的实现都是由函数来完成的。因此,函数的重新构建成为C语言重构的最重要的一环。
1、重新命名函数
代码首先是为人写的,其次才是为机器写的。函数的名称应该准确表达出它的用途。当我们维护代码时,通过名称猜出这个函数的用途可以节省大量的时间。想想自己的一些经历,通过费力的阅读代码才能明白,哦,它原来是干这个的!如果有个好的名称该多好,看看名字,就能大概得到这个函数的用途,有了这个大概情况,再去阅读代码,是那么的轻松、愉快。
有些人会想,我写的是C程序,不是Java语言。C语言要求的是简洁的名称,而不是Java那样,恨不能名字比实现的代码还长。我觉得,简洁不代表含糊不清。我们都有默许的规范,read就是代表读,取一个名字叫rfile(),我就会猜它是读文件;要是叫sfile(),谁知道它是什么?
2、提炼函数
过长的函数当中,多半会有很多繁杂的过程包含在里面。当我们需要不断的上下翻页来了解一个函数的功能时,很快就会不可耐烦,并且丧失信心。因此要想让函数变得容易让人看懂,就得尽量让函数简练。这就得把函数功能细化,不断的提炼函数。每个函数都有其基本的功能,使得函数复用的机会很大,复杂函数就是这些小函数的组合,这样复杂函数读起来就像一系列注释,通俗易懂。
3、提炼判断条件
有时候在判断条件当中,会遇到一个很长的表达式表示一个条件分支,这个表达式有时不光包含了“与”、“或”、“非”,还有查询函数,大于小于等等。要看懂这么一个表达式,首先要做的是查清楚括号的对应关系,然后查清楚各种运算符的运算优先级,最后在去考虑这个表达式代表的意思。
为了摆脱这种“与非”困境,我们要做的是把这个判断条件提炼出来,或者赋值给一个临时变量,或者彻底提炼成一个函数,变量或者函数的名字明确代表了判断条件的意思,例如isEmpty等等。这样在判断语句中,就可以一眼看出来这个分支是怎么一回事了。如果真要考虑效率问题,可以把提炼出来的函数作为inline函数。总之,要写出给人类看的代码,这个工作是很值得使用的。
4、简化函数参数
使用C语言编程,缺少强大的自动补齐工具(尤其是喜欢vi的人,虽然下面有补齐的插件,但是很少使用,也不大适应),如果函数的参数太多,根本记不住应该加的参数,编写代码时要不断的去翻看,降低了效率,尤其在多个人合作编程时,这种情况会尤为明显。但是这在c语言中有时候是很难避免的事情,总不能为了数值传递设置大量的全局变量吧。
我的办法是设置结构体,把意思相近的参数合并成一个整体,然后为这个结构体设置赋值函数。这样在一定程度上简化了主要函数的参数个数,便于记忆。
二、改进代码总体结构
1、解除全局变量的噩梦
在c语言编程中不可避免的要使用全局变量。传递数值、进行条件判断等等时,全局变量给我们带来了极大的方便,但同时也带来了噩梦。有时,全局变量根本不是自己预想的数值,而是被哪个莫名的代码悄悄修改了,要在那么多代码中查找这个作祟者简直痛苦不堪。
好的做法是把全局变量设置成static,只能在本文件中看到,然后编写访问/设置函数来控制变量的访问入口。查找函数总比查找一个小小的赋值语句简单的多,起码还有断点可用。退一步讲,如果使用多线程或者多进程没有断点可用,那么好的做法是使用宏定义编写访问控制点,在宏定义中可以加入打印,把这个语句在的文件和函数都打印出来,我就不信,找不到它,哈哈哈。
2、避免函数传递中的隐形bug
c语言的参数传递其实就是传递的一块内存,在函数执行时,在这块内存中,根据参数类型取得一块块相应大小的数据。这样就产生了隐形的bug。比方说,在没有记住函数参数类型的情况下,传递了一个结构体给它,结果该函数只是几个int类型的参数,这时编译器也不会报错给你,更恐怖的是代码照样可以运行,如果没有测试充分,没准代码还能正确运行。
这样一个隐形的bug会把你搞的很惨。要把它干掉,最简单的办法就是加入函数声明。灵活便利的c编译器,在函数调用上,没有强制要求加入函数声明,而函数声明尤其在调用另外文件的函数时,很重要。如果调用与声明的类型不一致,编译器会报错。这样这个隐形bug就不会在骚扰你了。所以,不要图省事,为了以后的和谐生活,声明你要调用的每一个函数吧。
3、调整函数位置
在多文件程序当中,函数的位置很重要,要根据每个文件完成的功能调整函数的位置,不能乱放。乱放的现象在那些临时加的被调用函数身上表现的非常突出(我经常就干这事)。乱放导致的后果就是,后面维护代码时,根据文件名去找相关的函数,根本没有,结果一搜发现它在一个内容完全不相干的文件中。为了不打乱思路,临时放就放吧,但是这个功能完了,一定要把这个函数重构到它应该在位置上。
对于重构来讲,想到的暂时就是这些,其实有一些是要开始编写时就要注意的。这也应了这么一句话:重构本来就应该是融入到日常的代码编写中。最高境界就是手中无重构,心中也无重构。