代码是由很多个函数(在面向对象的编程语言中叫方法)组成的。函数的可读性、可维护性决定着整个项目代码的质量。因此编写具有高可读性和高可维护性的函数是每个程序员的职责。
虽然绝大多数程序员都理解可读性和可维护性对代码质量的重要意义,可还是经常不自觉地写出冗长复杂的代码。这些代码不要说今后易于维护,就是读懂都很困难。很多复杂的代码都有一个共同特征,就是充满了上百甚至更多行的函数。函数越长,要理解函数的功能就越困难。同时,由于同一个函数里通过变量关联的代码可能相隔甚远,这也给代码维护增加了困难。
虽然绝大多数程序员都理解可读性和可维护性对代码质量的重要意义,可还是经常不自觉地写出冗长复杂的代码。这些代码不要说今后易于维护,就是读懂都很困难。很多复杂的代码都有一个共同特征,就是充满了上百甚至更多行的函数。函数越长,要理解函数的功能就越困难。同时,由于同一个函数里通过变量关联的代码可能相隔甚远,这也给代码维护增加了困难。
注释不一定能提高函数的可读性和可维护性
为了使代码容易读懂和容易维护,很多程序员都会选择在函数体内添加注释,用来描述一段代码的作用。这样写注释的动机是好的,然而注释并不一定能提高函数的可读性和可维护性,反过来注释还有可能让读代码的人更加疑惑。很多人在修改代码的时候,注意力都放在代码、功能上面,修改代码之后忙着马上编译、运行。只要功能和预期的一致就觉得可以了,至于与代码匹配的注释,往往会忘记同步修改。一旦注释和代码不一致,以后维护该代码的程序员就会非常困惑,不知道应该相信代码还是相信注释。
我们先看一个例子。下面的C#代码为某数据库服务生成连接字符串:
public string BuildConnectionString(DatabaseService dbService)
{
string connection = string.Empty;
// Only running and creating services have connection strings
if (dbService.State == State.Running
|| dbService.State == State.Creating)
{
connection = string.Format("server={0};uid={1};pwd={
{password}};port={2}",
dbService.ServerID, dbService.Name, dbService.Port);
}
return connection;
}
最开始的时候,只有正在创建的或者正在运行的数据库服务都有连接字符串,处于其他状态的服务的连接字符串都是空字符串。因此在实现该函数的时候,该项目组的程序员写了一个if语句来判断服务的状态。由于判断的逻辑稍显复杂,他还贴心地为该if语句加上注释,告诉其他人这个if语句的作用。
没过多久,需求发生了变更,只有正在运行的服务有连接字符串,正在创建的数据库服务的连接字符串也为空。这个改动很容易实现。于是很快代码就被改成:
public string BuildConnectionString(DatabaseService dbService)
{
string connection = string.Empty;
// Only running and creating services have connection strings
if (dbService.State == State.Running)
{
connection = string.Format("server={0};uid={1};pwd={
{password}};port={2}",
dbService.ServerID, dbService.Name, dbService.