http://www.zhlmmc.com (收藏,设为首页)
岂能尽如人意,但求无愧我心 (手机请访问 http://3g.dlog.cn/zhlmmc)
上一篇:盖茨的演讲 下一篇:感性与理性

谈谈代码风格

2008年3月1日(Saturday) 09点29分 作者: 虫虫 天气: 心情: 一般
// 好久不写日记了,太忙了,三月份应该会好一点
// 其实,漂泊在外的日子真的不好过,我一定会回国,早晚的事情


谈谈代码风格

by zhlmmc

 

前言

       刚刚阅读完《Java Puzzlers》,是一本好书。看完你会发现自己对Java的了解是多么的匮乏。书中一共有95puzzlers,详细记得的不多。我回忆一下,总结出以下三点:

<[if !supportLists]>1.       注意数字类型的溢出和精度问题<[endif]>

<[if !supportLists]>2.       能用Java Library提供的函数就不要自己重复劳动<[endif]>

<[if !supportLists]>3.       代码要简明易懂,不要用一些不常用的语法和trick<[endif]>

还有很多技术细节的问题记不起来了,大概碰上了能想起来。不过这和我这篇文章要讲的内容关系不大,我要讲的是代码风格,而不同于我们平时所说的代码风格。我不是要讲“format”比如大括号的位置,缩进,空行等等。我要讲的是如何让自己的代码逻辑更清楚,更容易阅读。

 

<[if !supportLists]>一、             方法的名字应当准确表述方法的功能<[endif]>

给方法取名很重要,特别是在别人要调用你的代码的情况下。如果方法的名字不能准确描述方法的功能,调用者会很迷惑。比如Java Thread类的一个反面教材“Thread.interrupted()”。这个函数会返回当前线程是否interrupted,并且清除当前的interrupted状态。所以你如果调用这个函数两次的话,返回的值有可能是不一样的。这个方法的名字应该叫做“Thread.getAndClearInterrupedState()”或者“Thread.clearInterrupedState()”。因为这里“clear”是主要操作,“get”省略并不影响使用。况且这个函数的返回值告诉调用者有“get”操作。

 

再来看一个例子:

public boolean connectToServer(){

       // Code for connecting to server

       // Code for reading message from server

       // Current thread is blocked until reading is finished.

      

       return true;

    }

   

    public boolean connectToServerAndWaitResponse(){

       // Code for connecting to server

       // Code for reading message from server

       // Current thread is blocked until reading is finished.

      

       return true;

    }

connectToServer”这个名字并没有告诉调用者这个函数会block当前线程。所以调用者会以为这个函数在连接上服务器以后会马上返回,然后执行后面的代码。但是真的调用的时候这个函数有可能被block,如果调用者没有这个函数的源代码的话就会很迷惑,找不到bug所在。“connectToServerandWaitResponse”这个名字明显的告诉调用者,“我会block,你可能需要一个新的线程去执行其他操作”。

 

       虽然方法的名字变长了,但是我们有现代化的IDE帮忙,再长的方法也不用自己输入,都是自动补全的。一个好的方法名字可以省去查看API的麻烦,何乐而不为?

 

<[if !supportLists]>二、             把逻辑简单的“if”放在前面<[endif]>

我们先来看两段代码:

public boolean checkValid(String name){

       if (name.contains("%")) {

           // 50 lines of code

       }

       else if (name.contains("?")){

           // 20 lines of code

       }

       else if (name.contains("$")){

           // 5 lines of code

       }

      

       return false;

}

 

public boolean checkValid2(String name){

       if (name.contains("$")) {

           // 5 lines of code

       }

       else if (name.contains("?")){

           // 20 lines of code

       }

       else if (name.contains("%")){

           // 50 lines of code

       }

      

       return false;

}

这两段代码的区别是三个“if”的顺序。第一段代码把复杂的“if”放在前面,第二段相反。哪段代码更清晰易懂呢?初一看觉得差不多,其实第二段代码比第一段更容易阅读。人在阅读条件代码的时候脑子里始终要记得当前代码的条件是什么,也就是现在在哪个“if”下面。如果把复杂的“if”放在前面,人读到后面可能都忘记了自己还在“if”语句里面。等读到“else”的时候可能会很想“恩前面那个条件是什么?”所以把简单的“if”放在前面能够减轻阅读代码的负担。先处理简单的逻辑,在读后面复杂逻辑的时候回忆简单的逻辑比较容易。

 

<[if !supportLists]>三、             能“return”的地方就“return<[endif]>

我们还是来看两段代码:

public boolean checkValid3(String name){

       boolean valid = false;

      

       if (name.contains("$")) {

           // 5 lines of code

           valid =  true;

       }

       else if (name.contains("?")){

           // 20 lines of code

           valid = true;

       }

       else if (name.contains("%")){

           // 50 lines of code

           valid = true;

       }

      

       return valid;

}

 

public boolean checkValid4(String name){

       if (name.contains("$")) {

           // 5 lines of code

           return true;

       }

       else if (name.contains("?")){

           // 20 lines of code

           return true;

       }

       else if (name.contains("%")){

           // 50 lines of code

           return true;

       }

      

       return false;

}

这两段代码的区别是第一段代码用了一个临时变量还保存返回值,第二段代码在每个“if”里面都直接返回。那个更好?显然第二个。先不说代码行数减少了,关键是阅读代码的时候脑子的负担减少了。第一段代码的每个“if”以“valid =  true;”结尾,人在读到这里的时候心里会想,“后面还会对这个valid做什么操作呢?”。而第二段代码的每个“if”以“return true;”结尾,人在读到这里的时候心里的包袱就放下了,一个case结束,可以集中精力在下面的cases了。其实不光是“if”条件语句,在“try catch”,“for”等等语句都一样的,在能return的地方就return能够让代码的逻辑更简单(有时候“continue”,“break”也能达到一样的效果),能减轻阅读的时候的思想包袱,因为return一个,逻辑就简单了一层。

 

<[if !supportLists]>四、             删掉不必要的“else”语句<[endif]>

你可能已经发现了,我们前面的例子可以更加简化一点:

public boolean checkValid5(String name){

       if (name.contains("$")) {

           // 5 lines of code

           return true;

       }

      

       if (name.contains("?")){

           // 20 lines of code

           return true;

       }

      

       if (name.contains("%")){

           // 50 lines of code

           return true;

       }

      

       return false;

}

我对“checkValid4”做了一些简单的修改,把所有的“else”去掉了。很明显,这些“else”都是多余的,因为我们在每个“if”中都“return”了。这样代码更清晰,一眼望去,三个“if”,表示有这里有三个cases。然后分别阅读每个case,读完一个扔一个,因为每个case之间都没有关联。加了“else”虽然不影响代码的逻辑,但是让人潜意识中感觉到这几个cases好像有关联,拖泥带水。

 

<[if !supportLists]>五、             尽量在声明成员变量的时候进行初始化<[endif]>

我们继续看代码先:

class Demo1{

    private List nodes;

   

    // 100 lines of other logic

   

    public Object getNode(int index){

       return nodes.get(index);

    }

   

    // 100 lines of other logic

   

    public void addNode(Object node){

       if (nodes == null) {

           nodes = new LinkedList();

       }

      

       nodes.add(node);

    }

}

 

class Demo2{

    private List nodes = new LinkedList();

   

    // 100 lines of other logic

   

    public Object getNode(int index){

       return nodes.get(index);

    }

   

    // 100 lines of other logic

   

    public void addNode(Object node){

       nodes.add(node);

    }

}

这两段代码的区别在于“nodes”成员变量的初始化的位置。我们假设这个类有200+行代码。先看Demo1,首先你看到这个类声明了一个成员变量“nodes”,但是没有被初始化。当你看到“getNode”这个函数的时候,你一定在想,“nodes”在哪里被初始化了?为什么这里可以直接用?然后你一定打断当前逻辑,去找“nodes”的初始化代码。为什么不在声明的时候直接初始化呢?这样就没有后顾之忧了。而且可以减少bug,保证在调用“nodes”之前,它一定已经被初始化,否则你有可能会遇到“NullPointerException”。当然,你可以说Demo1的这种做法叫做“Lazy Initialize”。不错,但是在这个例子中没有任何必要这么做。如果你的应用的初始化开销特别大,那另当别论。

 

结束语

       好了,现在来总结一下:

<[if !supportLists]>1.       方法的名字应当准确表述方法的功能<[endif]>

<[if !supportLists]>2.       把逻辑简单的“if”放在前面<[endif]>

<[if !supportLists]>3.       能“return”的地方就“return<[endif]>

<[if !supportLists]>4.       删掉不必要的“else”语句<[endif]>

<[if !supportLists]>5.       尽量在声明成员变量的时候进行初始化<[endif]>

好像还有很多可以写,一时半会儿想不起来了,等以后想到了再补充吧。

标签: Java 代码风格 
评论者: 999999 2008-3-3 01:58 (Monday)

说真的 你只有写技术性的文章 我才可以用看正常人的眼光来看你

。。。这真是我对你的赞扬啊。。。

评论者: 虫虫 2008-3-3 02:34 (Monday)
#1楼 999999 原帖:

说真的 你只有写技术性的文章 我才可以用看正常人的眼光来看你

。。。这真是我对你的赞扬啊。。。

点评:你能看懂我写的吗?

评论者: 91 2008-3-3 08:30 (Monday)
她肯定看不懂。。随便奉承两句的。。
不过几个代码规范写得确实是有道理的
评论者: 999999 2008-3-4 02:24 (Tuesday)

我真地看不懂啊

不过写的感觉上比较有条理 比你平常做的那些挫事好看多了

姓名: 
邮箱:  {可选}
网址:  {可选} 此评论只有我和写日记的人查阅
校验码: ... <我看不清楚>
网记为您提供手机和互联网同步的个人主页,带给你不一样的体验