编写优良的 Java 代码
介绍
您现在已经了解了很多 Java 语法,但是这并不是专业编程的真正主题。什么造就“优良的”Java 程序呢?
也许有多少专业 Java 程序员,这个问题就有多少种答案。但是我有一些建议,我相信大多数专业 Java 程序员都赞同,可以改善他们日常处理的 Java 代码的质量。开诚布公地说,我推崇像极限编程(Extreme Programming,XP)这样的灵巧方法,所以我的许多关于“优良”代码的观点一般是受灵活社团(agile community )的鼓动,尤其是受 XP 的影响。我依然认为大多数有经验的专业 Java 程序员会赞同我将在本节中提出的观点。
保持类最小
在本教程中,我们创建了一个简单的 Adult
类。即使在将 main()
方法移动到另一个类之后,Adult
还剩下 100 多行代码。它具有 20 多个方法,但是将它与您可能看到(和创建)的许多类从专业的角度来进行比较,它其实并没有做多少事情。这是一个小 类。具有 50 到 100 个方法的类并不少见。还有什么比拥有较少的类更坏的事情吗?可能不会再有了。当然,最好还是有满足自己需要的类。如果您需要几个本质上完成相同事情但带有不同参数的助手方法(比如 addMoney()
方法),那就是最好的选择。只是一定要限制需要的方法列表,除此之外,其他都按需行事。
一般来说,一个带有大量方法的类总是具有一些不属于这里的方法,因为这个庞大的对象所做的事情太多了。Martin Fowler 在他的 Refactoring一书中(参见 参考资料 中的链接), 将这称为 Foreign Method 代码味道。如果您有一个带有 100 个方法的对象,就应该好好想想,这个对象是否应该拆成多个对象。大类通常在大学里大行其道。Java 代码与之一样。
保持方法最小
小方法就与小类一样可取,并且原因也类似。
很多有经验的 OO 程序员对 Java 语言具有的苦恼之一就是,它提供大量的 OO 能力,但是却没有教他们如何做好 OO。换句话说,它给了他们很多绳子去捆绑自己(尽管至少没有 C++ 给他们的多)。能看到这一点的一个常见地方是 main()
方法离得很远的类,或者一个单个的名叫doIt()
的方法。仅仅因为能够 将所有的代码放在类中的单个方法中,并不意味着就应该 如此。Java 语言比很多其他 OO 语言具有更多的语法糖,所以一定的罗嗦是必要的,但是不要过了度。
思考一下这些超长的方法。滚动十屏代码去了解代码所做的工作是很艰难的。该方法做什么工作?您需要泡上一杯大大的咖啡花几个小时去研究才能知道。一个小的,甚至微小的方法是一个容易看懂的代码块。运行时效率不是要具有小方法的原因,可读性才是真正的目标。这将使得代码更加容易维护,并且在需要添加功能时更加容易更改。
将每个方法局限于执行一项工作。
给方法取好名称
我曾经见到过的最佳编码模式(忘了是在哪里见到的了)叫做揭示意图的方法名。下面两个方法名中哪个更容易一眼看出意图?
a()
computeCommission()
答案很明显。出于某些原因,程序员似乎讨厌长的方法名。过长的方法名无疑是不方便的,但是长到足够明了通常不是过长。我觉得像aReallyLongMethodNameThatIsAbsolutelyClear()
这样的方法名是可以接受的。如果在凌晨 3 点,我试图找出程序为什么不工作时,遇到一个名叫 a()
的方法,那我真想揍人。
额外花几分钟想出一个非常具有描述性的方法名;如果可能,考虑以这样的方式命名方法,即让代码阅读起来更像英文,即使这意味着添加附加的助手方法来做这项工作。例如,考虑添加一个助手方法以使得代码更加可读:
if (myAdult.getWallet().isEmpty()) { do something }
ArrayList
上的 isEmpty()
方法本身是有帮助的,但是我们的 if
语句中的布尔条件应该像下面这样得益于 Adult
上一个叫做 hasMoney()
的方法:
public boolean hasMoney() { return !getWallet().isEmpty(); }
然后我们的 if
语句读起来更像英文:
if (myAdult.hasMoney()) { do something }
这一技巧很简单,也许在本例中是微不足道的,但是当代码变得越来越复杂时,它就会显露出惊人的威力。
保持类的数量最少
XP 中关于简单设计的其中一个指导方针是,用尽可能少的类完成一个目标。如果您需要另一个类,尽管添加就是了。如果另一个类将使得代码更简单或者简化您的意图表达,那么就添加这个类吧。但是没有理由只是为了具有而具有类。当然,通常项目早期比结束时具有的类要少,但是一般将代码重构成更多的类比组合类更容易。如果您有一个具有大量方法的类,那么分析一下,看是否有另一个对象陷入在其中,正在等待出去。如果有的话,就创建一个新的对象。
在我经历的几乎所有 Java 项目上,没有人害怕创建类,但是我们也总是试图在不降低意图清晰度的情况下,减少类的数量。
保持注释的数量最少
我过去常常在代码中编写很多的注释,读起来就像一本书。后来我变得聪明一些了。
每一个计算机科学程序、每一本编程书籍和我知道的很多程序员,都要您给代码编写注释。在有些情况下,注释是有帮助的。在许多情况下,注释使得代码维护更加困难。想想您更改代码时必须做什么。有注释吗?如果有的话,您最好更改注释,否则它会可怕地过期,甚至随着时间的推移,根本就不再能够描述代码。依我的经验,几乎会使维护时间加倍。
我的经验法则是:如果代码太难阅读和理解而需要注释,我就需要使它足够清晰,从而不需要注释。代码可能会太长,或者做太多的事情。如果这样的话,我就简化它。代码可能太隐晦。如果这样的话,我就添加助手方法,使之清晰。实际上,在与同一团队的其他成员一起进行 Java 编程的三年当中,我所编写的注释屈指可数。保持代码清晰!如果您需要系统或者某个特定组件的全景描述,就编写一个简短的注释来描述。
罗嗦的注释一般比较难维护,通常不及一个小的、编写良好的方法那么好地表达意图,并且很快就会过期。根本不要过分依赖注释。
使用一致的风格
编码风格实际上是您的环境中必然的并且可接受的东西。我甚至不知道哪一种风格可以称为“典型的”。这通常是个人品味问题。例如,下面是一些让我看到会难受的代码:
public void myMethod() { if (this.a == this.b) { statements } }
为什么这会烦扰我呢?因为我个人偏向于反对那些添加我认为不必要的代码行的编码风格。Java 编译器认为下面的代码与刚才的代码完全相同,而我减少了几行:
public void myMethod() { if (this.a == this.b) statements }
二者没有“对”或“错”之分,只是一个比另一个短一些。那么当我与某个喜欢第一种形式的人一起编码时会怎么样?我们进行协商,挑选出一种来,然后一直坚持用这一种。惟一绝对的风格规则是一致性。如果一个项目上的每个人都用不同的风格,那么阅读代码将变得很困难。挑选一种风格并且不要改变。
避免 switch
一些 Java 程序员对 switch
语句情有独钟。我曾经认为它们很好,但是后来我认识到了,一个 switch
实际上就是几个 if
,并且通常意味着条件逻辑出现在代码中的多个地方。这是代码重复,是应该禁忌的。为什么?因为在多处具有相同的代码使得代码比较难更改。如果我在三处具有相同的 switch
,并且想要更改对某个 case 的处理,我就得在三处更改代码。
现在,如果您可以重构具有单个 switch
语句的代码,那会怎么样呢?很好!我不相信使用它有什么坏处。在有些情况下,它比嵌套的 if
更清晰。但是如果您看到它出现在很多地方,就有了应该解决的问题了。防止这一问题的一个容易的方法是,避免 switch
,除非它对于该工作是最佳的工具。依我的经验,它很少是最佳的。
是 public 的
我把最具争议的建议放在最后。做一下深呼吸。
我相信您会反对让所有的方法都是 public
的。实例变量应该是 protected
的。
当然,许多专业程序员会害怕这种想法,因为如果任何东西都是公共的,那么任何人都可以更改它,也许会以未授权的方式更改。在任何东西都是公共可访问的世界中,您就不得不依赖于程序员纪律,以确保人们在其不应该访问时不会访问其不应该访问的东西。但是在编程生活当中,很少有什么事情比想要访问一个不可见的变量或方法更受挫的了。如果您对代码中您设想其他人不应该访问的东西限制访问,您就是在设想自己无所不知。这在大多数时候是一个危险的假设。
当使用其他人的代码时,这种受挫感会经常出现。您会看到一个方法刚好做您想要做的工作,但是它不是公共可访问的。有时,这有一个很好的原因,并且使得限制可访问性有意义。但是有时,不 public
的惟一原因是,编写代码的人这样想“没有人需要访问该代码”,或者他们这样想“没有人应该访问该代码,因为……”,于是他们并没有很好的理由。许多时候,人们使用 private
是因为它可用。不要这样做。
使方法是 public
的,变量是 protected
的,直到您有一个很好的理由限制访问。
追随 Fowler
现在您知道了如何创建优良的 Java 代码,以及如何保持它优良。
关于该主题,业界最好的书籍是 Martin Fowler 编著的 Refactoring (参见 参考资料 中的链接)。它读起来甚至很有趣。重构(refactoring) 的意思是在不改变代码结果的情况下,更改现有代码的设计。Fowler 谈论了请求重构的“代码气味”,并且详细介绍了用于改变“代码气味”的各种技巧(或者“重构”)。依我的观点,重构和编写一次通过测试的代码的能力(参阅 参考资料)是新手程序员要学习的最重要的技能。如果每个人都擅长于这两点,那将彻底改变行业当前的局面。如果您 擅长于这两点,就会比较容易找到工作,因为您能比其他人产生更好的结果。
编写 Java 代码相当简单。编写优良的 Java 代码则是一门手艺。倾力成为一个手艺人。
结束语
在本教程中,学习了 OOP 和用于创建有用对象的 Java 语法,并了解了一个有助于控制开发环境的 IDE。您可以创建做很多事情的对象,尽管不是您能想像到的每一件事情。但是您可以用几种方式扩展自己的知识,包括通过其他 developerWorks 教程来研究 Java API 和探索 Java 语言的其他功能。请参见 参考资料 中的链接。
Java 语言无疑不是完美的;每种语言都有它的特点,而每个程序员都有自己的喜好。但是 Java 平台是一个很好的工具,可以帮助您编写非常好的专业程序。