原文地址:
http://www.urubatan.info/2008/11/commenting-source-code-is-only-for-the-weak/
你们中的许多人可能不同意我的看法,但是当你阅读完了本篇文章,你将可能同意这点:
注释你自己的源代码是你写的代码不好看的标志。真正的程序员是写他人能读的代码!
但是记住我谈论的是说明代码是干什么的那样的注释,而不是诸如Javadoc那样的文档。
举一个例子:如果你不了解抑或不是一个Java程序员,你能很快地说出以下的代码是干什么的吗?
public String write(StringBuilder fle, StringBuffer con) {
File f = new File(fle.toString());
FileReader fr = new FileReader(f);
BufferedReader br = new BufferedReader(fr);
String lin;
while((lin=br.readLine())!=null){
con.append(lin).append("\n");
}
return con.toString();
}
困难吗?这只是一段非常简单的代码,但是我们可以让它变得再好一点...
public String read(StringBuilder fle, StringBuffer con) {
//Opens the file with the name container in the fle parameter
File f = new File(fle.toString());
//Create a file reader, then a buffered reader to make our work easier
FileReader fr = new FileReader(f);
BufferedReader br = new BufferedReader(fr);
String lin;
//Read each line of the file until it is null
while((lin=br.readLine())!=null){
//Put the content read into the buffer pointed by the parameter "con"
con.append(lin).append("\n");
}
//The caller already have the content, because he created the buffer, but I'll return the string anyway
return con.toString();
}
简单一些了吧,对吗?现在你读着代码中的注释,并且也理解了它,但是代码仍然是不好看。
这种类型的注释就是我在现在谈论的。注释你的源代码表明你是一个非常糟糕的程序员(一个小例外就是遇到不能将代码分解成小片断的很复杂的算法)。
当你在注释你的代码,你实际上是在用一种HACK的方式向他人掩饰你所做的不合格。这表明了你不会写干净的代码。不会写让其他程序员可读的代码!
现在我就就上面写的代码,来重构一下其中的难读的方法。
public String readFileContents(File fileToRead) {
boolean canReadFile = fileToRead.exists();
if(!canReadFile)
return "";
StringBuilder buffer = new StringBuilder();
BufferedReader readerForFile = openBufferedReaderForFile(fileToRead);
readFileContetIntoBuffer(buffer,readerForFile);
closeFileReader(readerForFile);
return buffer.toString();
}
private BufferedReader openBufferedReaderForFile(File fileToRead){
return new BufferedReader(new FileReader(fileToRead));
}
private void readFileContetIntoBuffer(buffer,readerForFile){
String line;
while((line=readerForFile.readLine())!=null){
buffer.append(line).append("\n");
}
}
private void closeFileReader(readerForFile){
readerForFile.close();
}
现在,你注意一下名为readFileContents的方法,你已经知道了它是干什么的,另外,方法的代码很容易理解的,如下面意思所表明的意思:
引用
如果不能读文件,返回null,
为名为fileToRead的File打开Buffered Reader,
将文件的内容读到缓存中去,关闭File Reader readerForFile,
返回buffer.toString();
我所表达的说明了:如果你懂英语,你就能读懂代码,而几乎每个程序员都知道英语,对吗?(
我注:当然非英语国家...)
我曾看到有人写了如下的很糟糕的代码,如同下面这个样子:
public String write(StringBuilder fle, StringBuffer con) {
File f = new File(fle.toString()); FileReader fr = new FileReader(f); BufferedReader br = new BufferedReader(fr);
String lin; while((lin=br.readLine())!=null){ con.append(lin).append("\n"); }
return con.toString();
}
这个比我的版本的行数少,但是你要上一段困难的时间试图弄懂这个糟糕的程序员写出的如上的代码片断。
当然,我使用的这个例子非常简单,并且我知道写出清析易读的代码是需要实践的...
但是我给你一个提议,不是一个真正的提议,是一个练习而已...
我写了一个如下的代码示例,你试着将它变成可读的。我会在一两天后贴出我的解决方案。
你们可以直接在文章回复中贴出你们的重构代码。
package blog;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class VeryBadlyNamedFile {
private static final char[] asdfg = new char[] {'I', ' ', 'c', 'a', 'n', ' ', 'd', 'o', ' ', 'v', 'e', 'r', 'y', ' ', 'u', 'g', 'l', 'y', ' ', 'c', 'o', 'd', 'e'};
private String an;
private BufferedReader rfsdw;
private FileReader temp;
public VeryBadlyNamedFile(String an, BufferedReader rfsdw, FileReader temp) {
super();
this.an = an;
this.rfsdw = rfsdw;
this.temp = temp;
}
public void doIt() throws IOException {
ctfiidne();
startDoing();
try {
canIDoAnyThing();
} catch (RuntimeException yicdet) {
nowReallyDoIt();
}
}
private void nowReallyDoIt() {
firstDoTheOtherThing();
reallyDoItInternal();
}
private void firstDoTheOtherThing() {
rfsdw = new BufferedReader(temp);
}
private void reallyDoItInternal() {
while (true) {
try {
imDoingIt();
} catch (Exception e) {
break;
}
}
}
private void imDoingIt() throws Exception {
String s = rfsdw.readLine();
if (s == null)
throw new Exception("hahaha, I bet you did not understood the code");
System.out.println(s);
}
private void ctfiidne() throws IOException {
File a = new File(an);
if (!a.exists()) {
FileWriter wrfedsd = new FileWriter(a);
wrfedsd.write(asdfg);
wrfedsd.close();
}
}
private void canIDoAnyThing() {
if (new File(an).exists() && new File(an).canRead() && new File(an).canWrite())
throw new RuntimeException();
}
private void startDoing() throws FileNotFoundException {
File f = new File(an);
temp = new FileReader(f);
}
}
现在用一个主程序执行上面的代码:
package blog;
import java.io.IOException;
public class Main {
public static void main(String[] args) throws IOException {
new VeryBadlyNamedFile("c:\\anyFile.any",null,null).doIt();
}
}
以上的这些例子用的是Java代码,但是本观点适用于任意语言,如果你编程,并且用注释来解释你的代码,那么你是一个可怕的程序员。
快点祈祷上帝的帮助,乞求下一个读了你废话般的代码的的人不是一个知道你住处的连环杀手!
PS.:我真的不认为你不应该注释你的代码,但是 如果你的代码如果没有注释就理解不了,那表明你还不会写程序。
PS2.: 我觉得我应该来些更好的例子。
PS3.: 写出好代码的秘诀就是你写代码即使你知道下一个阅读你代码的人是一个连环杀手,并且知道你住在哪儿。(个人理解就是我们时刻要记住写出的代码是要给人家看的)
------我的分割线------
在现实生活中,我常常遇到还没有达到如作者说的那样
public String write(StringBuilder fle, StringBuffer con) {
File f = new File(fle.toString()); FileReader fr = new FileReader(f); BufferedReader br = new BufferedReader(fr);
String lin; while((lin=br.readLine())!=null){ con.append(lin).append("\n"); }
return con.toString();
}
命名的级别的程序员,变量名完全是一时想到个什么字母单词之类的就敲出的,并且连注释也不写!这样人,阅读了他代码的连环杀手一定会让他死得很难看!
感觉写Ruby代码更是要如此,比如@posts表示你取出来的是一个文章列表,但是我的旁边的那个同事就不注意这些细节,他用@post(他英语不好,但是我觉得是他赖得去理这些)
还有一次是大家一起做一个rails项目,另一个同事做好了他那一块,我要接着在他做的页面中加一个功能,我看他页面中有一个@user,我看正合我用,就直接用,却出错了,找错误花了我好一些时间,原来@user实际上Company类型的,他为什么不直接用@company啊?现在我要用user,是
@user.user,你们说,这代码看着像个什么啊? ORZ,他为什么这样写的,因为他做的那个功能叫搜索用户,在页面上显示的是用户的公司。ORZ,我是无语了。
说实话,用rails做项目,我出的错好多都是因为成员们命名不规范导致的,找到这些错误后,很无语...