穷究链表(四)--链表实现前的思考
C是没有类的概念的,因此其所有的应该就是变量和函数,如果我们仅仅是编写用来自己使用的链表程序,我们可以定义一个全局的变量,然后用函数来操作这个变量。不过这些函数就无法给其他调用者来使用,如果要给其他调用者使用,我们必须将链表本身作为函数的参数来进行传入,而在调用者处定义好链表。
不过,这样是最优美的实现吗?此时,如果是你来做。你会怎么思考?
之后就是考虑其特性的时候,我们需要实现链表的多少特性?
这里暂时准备实现1.添加 2.删除 3.遍历 4.倒序 5.两个链表合并 6. 查找特定节点,如果还有一些比较重要的,可以随时进行添加。
这里稍微讲一下C的命名规则,C的规则每个公司都有其自己的规定,但是要求基本基于两点,一是清晰,二是尽量的简短。
清晰大家当然比较明了,可以增加代码的可读性,那简短是为什么呢?按照我的理解,是延续了大型机时代的一种习惯,当时屏幕的分辨率可是很低,800*600也是很牛B的。然后编程也需要远程到solaris等UNIX或类UNIX系统上来做,其登陆的标准终端一般是80个字符长,这样就需要我们的函数名字比较简短,不要换行或者拖动就在一个界面上完全显示出函数的调用。
这比DOS时代对内存的锱铢必较好太多了,但是也还是挺吝啬的啦,所以才有point被缩成pt,size变成sz这样的命名,rectangle这样十恶不赦的名字,是绝对绝对不能存在的,如果你仔细阅读过一些C或者C++的代码的话,应该会有这样的感觉。
当然,这里命名就不一定要遵守这样的规则了,如果你在公司中,那就请遵守各个公司的命名规则手册吧。
然后就是考虑,我怎么定义这些函数。函数包括函数名,参数和返回值这三个要素。对于函数名,我们只需要对应取一个有意义的名字就可以了。由于是使用C语言,所以我们甚至不必考虑访问限制,虚函数等C++范畴的东西。那就是考虑对实现该函数的功能,我们需要怎样的参数和返回值。而更进一步的思考就是,我们怎么设置参数和返回值,来提高效率,或者接口更加明确清晰。当然,其实在工程中,到这一步来考虑上面的问题,就是你们公司的流程问题了,这些应该也是设计中的问题,而不是实现中的问题。很多人都在思考设计怎么做,这就是设计,当然,这个是属于详细设计了。但是,没有经历详细设计,就直接做概要设计的家伙,能够被信任吗?能够做出好的系统吗?反正我挺怀疑的。当然,有老板用他,那是他的本事了。而能够思考到这一步,我想,你应该就是一位合格的程序员了。
当然,真正的设计没有这么简单,真正的设计也不是由你来定,大型的系统都有架构师,我们的设计还没有到那层。至于模块的设计,也不是你的一言堂。你需要和调用你这个函数的哥们们商量,hi,我这么写,你觉得怎么样?还是你有些什么特殊的要求。大家坐下来讨论一下。如果你的函数被很多人调用,那就需要考虑到所有调用者的需求,来处理所有的情况了。复杂性就是这么一步一步被加高的。
而软件的扩展性也在于这里,如果我一开始的思路定好了,而且适合现在的要求,并且实现了,现在出现一个新的需求,一个新的调用,居然我现在的参数不能满足他,(你说,他出来捣啥乱啊,本来就够乱的了)。那我就需要修改接口,同时通知所有相关模块,不好意思啊,兄弟,我也不想的,不过没办法,大家这么这么改,这么这么调用。所以,如果你看到维护了10年20年的代码,感觉片断很好看,但是再去更上一层看,就感觉很乱,不要感觉奇怪,这是妥协的结果。
对于添加一个节点,那我就需要考虑其参数应该是一个新的节点,这里有几种选择,我可以只输入一个节点中存储数据的值,而在函数内部进行内存分配,创建新节点的工作,这样一来,我就考虑到是不是需要增加一个创建新节点的函数呢?另外一种选择是,我传入的就是一个新的节点的地址,那函数内部所需要做的就是将其链入链表即可。那这里就又有问题了,我是链入到链表的什么地方呢?是链入到头部,尾部,还是我指定的地方呢?如果我需要指定位置的话,那就需要我增加一个参数来做。
而对于这个参数,我就要确定,链表是从0开始计数,还是从1开始计数呢,这就相差1个了呀,所以就要有一个约定。而这个约定,如果没有注释,同时在文档中没有体现出来的话,在以后维护代码的时候,就会有些麻烦,而这样的约定多了,代码也就成了天书,没有人愿意去看。
然后需要对于参数进行一些必要的判断,也就是确定是否传入的值是有效的,可以使用assert宏等手段(这里不清楚assert宏是windows自身的,还是C库中的,请自己挑选使用,当然后面我会在程序中一一实现,但是现在也不去慢慢验证)。
接下来是返回值,是不返回,还是返回错误码,还是做什么具体的操作。当这些思考好了,差不多这个函数的实现也了然了。当然,这里还是比较简单的情况,复杂的还有与其他函数的交互,调用其他函数或者被其他函数调用的话,又更复杂一些,如果牵涉到一些全局变量,那又更复杂一些。另外,如果又有定时器或者回调的话,又多了一些其他考虑的事情。当你基于这个系统考虑得越多,越周全,那你对这个函数的实现就越有把握。同时也更能保证函数实现的健壮性。毕竟,没有人愿意写一堆烂代码出来。不过,大家都是这么走过来的,呵呵,不愿意写烂代码,怎么能知道问题所在,将其变为好代码呢。
每一个需要实现的函数都需要经过这样的思考,然后如果有多种选择的话,选择一个自己比较钟爱的(当然,要通过调用者那一关,不过,这里调用者和实现者都是我自己,那就随便选择了)。最后确定好各个参数和返回值。
这是我觉得在实现前需要经历的思考步骤,你觉得呢?如果是你来实现的话,你会怎么做呢?