编程的智慧 1

艺术与编程

编程是一种创造性的工作,是一门艺术。精通任何一门艺术,都需要很多的练习和领悟。
王垠(作者)根据自己的编码经验,总结了以下内容,欢迎探讨。(原文删减版)
原文链接:http://www.yinwang.org/blog-cn/2015/11/21/programming-philosophy 

反复推敲代码

  代码量与编程水平没有直接关系。如果总是匆匆写出代码,却从来不回头去推敲,修改和提炼,其实是不可能提高编程水平的。好的程序员,他们删掉的代码,比留下来的还要多很多。如果你看见一个人写了很多代码,却没有删掉多少,那他的代码一定有很多不足。
  
  优秀的代码是不可能一蹴而就的。就算再厉害的程序员,也需要经过一段时间,才能发现最简单优雅的写法。有时候你反复提炼一段代码,觉得很完美了,可是过几个月后,又会发现好多可以改进和简化的地方。这跟写文章一模一样,回头看几个月或者几年前写的东西,你总会发现一些改进。所以,如果反复提炼代码已经不再有进展,那么你可以暂时把它放下。过几个星期或者几个月再回头来看,也许就有焕然一新的灵感。这样反反复复很多次之后,你就积累起了灵感和智慧,从而能够在遇到新问题的时候直接朝正确,或者接近正确的方向前进。

写优雅的代码

  人们都讨厌“面条代码”(spaghetti code),因为它就像面条一样绕来绕去,没法理清头绪。那么优雅的代码一般是什么形状的呢?经过多年的观察,我发现优雅的代码,在形状上有一些明显的特征。
  
  如果我们忽略具体的内容,从大体结构上来看,优雅的代码看起来就像是一些整整齐齐,套在一起的盒子。如果跟整理房间做一个类比,就很容易理解。如果你把所有物品都丢在一个很大的抽屉里,那么它们全都混在一起。你就很难整理,很难迅速的找到需要的东西。但是如果你在抽屉里再放几个小盒子,把物品分门别类放进去,那么它们就不会到处乱跑,你就可以比较容易的找到和管理它们。
  
  优雅的代码的另一个特征是,它的逻辑大体上看起来,是枝丫分明的树状结构(tree)。这是因为程序所做的几乎一切事情,都是信息的传递和分支。你可以把代码看成是一个电路,电流经过导线,分流或者汇合。如果你是这样思考的,你的代码里就会比较少出现只有一个分支的if语句,它看起来就会像这个样子:

if (...) {
  if (...) {
    ...
  } else {
    ...
  }
} else if (...) {
  ...
} else {
  ...
}

  注意到了吗?在我的代码里,if语句几乎总是有两个分支。它们有可能嵌套,有多层的缩进,而且else分支里面有可能出现少量重复的代码。然而这样的结构,逻辑却非常严密和清晰。在后面我会告诉你为什么if语句最好有两个分支。

写模块化的代码

  模块化并非将代码放到多个文件或目录中,模块化更多指的是逻辑意义上的分块儿。一个模块应该像一个电路芯片,它有定义良好的输入和输出。实际上一种很好的模块化方法早已经存在,它的名字叫做“函数”。每一个函数都有明确的输入(参数)和输出(返回值),同一个文件里可以包含多个函数,所以你其实根本不需要把代码分开在多个文件或者目录里面,同样可以完成代码的模块化。我可以把代码全都写在同一文件里,却仍然是非常模块化的代码。

  想要达到模块化,需要做到以下几点:

  1. 避免写太长的函数。一个函数尽量不要超过笔记本电脑的屏幕所能容纳的行数,这样可以不用滚屏看完整个函数。
  2. 制造小的工具函数。提取重复的代码,可以简化主要函数里面的逻辑。
    有些人不喜欢使用小的函数,因为他们想避免函数调用的开销,结果他们写出几百行之大的函数。这是一种过时的观念。现代的编译器都能自动的把小的函数内联(inline)到调用它的地方,所以根本不产生函数调用,也就不会产生任何多余的开销。
  3. 每个函数只做一件简单的事情。
  4. 避免使用全局变量和类成员来传递信息,尽量是用局部变量和参数。有些人写代码,经常用类成员来传递信息,就像这样:
class A {
   String x;

   void findX() {
      ...
      x = ...;
   }

   void foo() {
     findX();
     ...
     print(x);
   }
 }

  首先,他使用findX(),把一个值写入成员x。然后,使用x的值。这样,x就变成了findX和print之间的数据通道。由于x属于class A,这样程序就失去了模块化的结构。由于这两个函数依赖成员x,它们不再有明确的输入和输出,而是依赖全局的数据。findX和foo不再能够离开class A而存在,而且由于类成员还有可能被其他代码改变,代码变得难以理解,难以确保正确性。
  
  使用局部变量而不是类成员来传递信息,这两个函数就不需要依赖于某一个class,而且更加容易理解,不易出错:

 String findX() {
    ...
    x = ...;
    return x;
 }
 void foo() {
   String x = findX();
   print(x);
 }

猜你喜欢

转载自blog.csdn.net/gnd15732625435/article/details/80299779