先说什么叫可重用性?顾名思义,可重用性就是指可以重复使用的特性.很多编程新手对这个概念理解不深刻.觉得代码写好了,要重复使用不就是复制再粘贴就行了吗.最多再改几个小地方.
然而这并不是代码可重用的体现!
代码可重用的精髓,我认为可以用unix哲学的两个点来说明:
- Write programs that do one thing and do it well. 写出来的代码只做一件事,并且把这件事作好。
- Write programs to work together. 写出来的代码,相互之间要一起协作。
举个例子:
比如现在有个需求,要求自己实现strlen函数,并求出一个字符串的长度,要求结果以:"the lenght of string is: ** “的形式打印出字符串的长度值.很多编程经验不多的人一看需求,心想这还不简单.信手拈来–以下是他们写的代码:
#include<stdio.h>
int main(int argc, char *argv[])
{
int i = 0;
char *pstr = "hello";
//这个循环体意思是从字符串第一个字符起计数,只遇到字符串结束标志'\0’才停止计数
while((*pstr++)!='\0')
{
i++;
}
printf("the lenght of string is:%d\n",i);
return 0;
}
代码是实现了需求,但从设计思想的层面上看,这样写代码并不可取.这段代码有个最严重问题:
代码并没有把'求字符串长度这件事'单独地放在一个函数中.而是全部放在main()函数中现实现.这给以后的调试带来了很多麻烦.代码简单点还勉强应付,要是在main()函数中写上几百上千行代码,调试的时候晕头转向是免不了的!
解决的方法,可以参看一下unix的哲学-写出来的代码只做一件事,并且把这件事作好。这句话里"写出来的代码"在c语言中,可以理解为写出来的函数的意思.
所以,改进的第一步是,先把作这一件事儿的代码作封装.即把这部分的代码单独写成一个函数,再由其他函数调用它.请看改进后的代码:
#include<stdio.h>
int mystrlen()
{
char *pstr = "hello";
int i=0;
//这个循环体意思是从字符串第一个字符起计数,只遇到字符串结束标志'\0’才停止计数
while((*pstr++)!='\0')
{
i++;
}
printf("the lenght of string is:%d\n",i);
}
int main(int argc, char *argv[])
{
mystrlen();
return 0;
}
这个版本的代码把真正干活的代码封装成了一个函数.封装是使代码层次清晰,逻辑清楚的重要手段.我们在main()函数中只是调用了这个干活的函数,就实现了我们的需求.封装的便利性还体现在调试代码的时候,如果要分析具体干活的代码,可以跟进去看,如果不需要跟进可以一跃而过.
对于调试以及分析代码来说.封装手段必不可少!
然而,这样写的代码还是离可重用有相当的差距.因为每次调用mystrlen()函数都是计算同一个字符串的长度.显然,我们在其他工程中不太可能都是计算"hello"这一个字符串.那可能有人要说,到时我改不就得了嘛,比如把”hello”改成"i love china”,不是也很方便吗?
话虽如此,但要修改代码不符合可重用的基本原则.而且上面的代码逻辑简单还好,万一代码的算法比较复杂,还得程序员理清代码的思路才知道怎么修改.
所以,代码还是有可以改进的地方的,我们再修改一个新的版本:
#include<stdio.h>
int mystrlen(const char *pstr)
{
int i=0;
//这个循环体意思是从字符串第一个字符起计数,只遇到字符串结束标志'\0’才停止计数
while((*pstr++)!='\0')
{
i++;
}
printf("the lenght of string is:%d\n",i);
}
int main(int argc, char *argv[])
{
char *pch="hello";
mystrlen(pch);
return 0;
}
这个版本中,我们把干活的函数封装起来,并且暴露出一个接口,即mystrlen(const char *pstr).只要任何函数调用这个函数接口,传入他想求长度的字符串的指针.mystrlen()函数都能完成工作.
改到这里,好像已经趋于完善了,对吗?
不!!!
如果这时需求改了,变成了要求输出的长度的形式为:”+++++len is:5++++++”,即输出结果前后都有一串其他的字符来修饰.可能编程新手觉得那是不是再接着修改mystrlen()里面的代码?如果是这样的话,那和前一个版本的代码也没什么区别.可重用就是直接拿来就用.涉及到要修改代码的,统统不合适可重用原则.
那应该怎么修改呢.我们看下unix的那两点哲学:
- Write programs that do one thing and do it well. 写出来的代码只做一件事,并且把这件事作好。
- Write programs to work together. 写出来的代码,相互之间要一起协作。
只作一件事儿-在这个案例中,应该封装一个函数,这函数只作一件事儿,就是求长度.至于要显示成什么样,那再封装另一个函数来完成.这也是unix遵循的原则:简洁为美!
ok,看我们怎么修改代码:
#include<stdio.h>
int mystrlen(const char *StrDest)
{
int i = 0;
while((*StrDest++)!='\0')
{
i++;
}//这个循环体意思是从字符串第一个字符起计数,只遇到字符串结束标志'\0’才停止计数
return i;
}
int dowork1()
{
char *str="hello,world";
int len = mystrlen(str);
printf("the lenght of string is:%d\n",len);
}
int dowork2()
{
char *str="hello,world";
int len = mystrlen(str);
printf("++++len is:%d++++\n",len);
}
int main(int argc, char *argv[])
{
dowork1();
dowork2();
return 0;
}
在最终的版本中,我们把求字符串长度的逻辑代码封装成一个小函数.这个函数简单至极,而且只作一件事-即把字符串的长度作为结果返回给调用它的父函数.其他事儿一概不管!
而在dowork1()中,调用mystrlen()函数求出相应字符串的长度后,再按照自己设想的形式输出结果.大家可以看到,dowork1()函数因此也变成很简洁.简洁的一大好处是,条理清晰,调试方便!如果要改变输出结果的形式,那再重新定义另一个函数dowork2(),这个函数里也同样调用了mystrlen()函数,并把结果按设想的作一个输出.通篇代码我们都没有对mystrlen()函数进行任何的修改.因为他只负责完成求长度的工作,工作越简单,就越能适配更多的场合.毫无疑问,mystrlen()作到了!
而函数之间要配合工作,就能实现最终的需求.请看这条哲学:
- Write programs to work together. 写出来的代码,相互之间要一起协作。
在main()函数中,需要实现什么功能,则调用相应的函数即可.main()函数在c语言中是一个统筹全局的函数,应该由它调配组合各种功能的函数实现不同的需求,而不应该把过多的逻辑代码入在其中.这样调试起来就会更得心应手!
经过这几个版本的修改,我们明白了封装的重要性!如果以后在其他工程中需要求字符串的长度,上面的代码中,哪个函数可以直接移过去就能重复使用了.相信你应该明白了!
总结:要想代码可重复使用,就要学会封装.封装的原则是:相关的代码写成一个函数.其他逻辑封装成其他的若干函数.只要函数的功能切分得足够细,那么它们之间的各种调配组合就能足够灵活.也更能适应更多的场合.
用玩积木来打个比方:一块块的积木堆叠成的一堵墙,这堵墙如果到了其他的小朋友的手中,不见得能直接就用上去.因为他们堆的城堡可能并不适合这堵墙.但如果这堵墙是拆分成足够细的单元.那其他小朋友才适合对它们进行"再创造".
所谓的代码可重复使用的思想,不过如此!