代码洁癖系列(三):整洁的类和函数

前面我们讨论了什么样的命名更能够让你赏心悦目,今天来讨论一下面向对象编程过程中最重要的环节,编写类和函数。我们仍然用Java来演示,什么样的类和函数才算是整洁的。

首先讨论函数,函数定义好了,类也就容易了。

短小

相信大家在读代码的时候都会遇到过冗长的函数定义。没有的话可以私信我,我把原来写过的一段300+行的函数发给你,不过不要问我这个函数是做什么的,因为我也忘了,而且不想回顾。当然如果你足够耐心研究出来了,请教教我。

言归正传,为什么函数要短小呢,如何才能是自己的函数更加短小?第一个问题我也无法证明,只能告诉你短小的函数看起来更加清晰,更加容易理解。那怎么才能让函数变得更加短小呢?很简单,抽离方法。将一些代码抽离成另一个函数。什么样的长度才是合适的呢?我认为不必过于追求短。这里的长度我们可以以代码块的层来定义,对于下面这种代码相信任何人看了都会崩溃吧。

1
2
3
4
5
6
7
8
9
10
11
12
13
public void doSomething() {
for() {
...
while() {
...
if() {
...
}else {
...
}
}
}
}

所以每个函数中有一层或两层为最佳,每层代码块最好不超过3行。这是我认为最佳的函数长度,当然,这个也可以根据个人习惯稍作调整。

只做一件事

如果说长度还可以根据个人习惯,那么“只做一件事”的要求应该是大家都应该遵守的公约了。如果一个函数中做了太多的事,那么代码阅读起来的难度将会成倍增加,而且文档书写难度同样增大。还有就是给其他代码调用造成不便。比如我定义了函数A做了1和2两件事,函数B想做2和3,怎么办?这时B只能再写一遍A中做2的代码。而这样就会有大量重复代码出现,不但增加工作量,对日后的维护工作也造成很大的负担。而把1和2分别定义为函数C和函数D的话,只需要在AB中分别调用就可以了。

命名

这里不多解释,函数的命名需要具有描述意义,函数越短也就越容易描述。

函数参数

参数数量越少越好(这个我目前也没有做到),究其原因,首先是读代码时每次都要搞清楚每个参数的意义,所以自然越少越好。另一方面就是为测试的同事提供方便,如果有多个函数,测试的同学就需要考虑更多的测试用例对其进行覆盖。如果一个函数有3个以上的参数,那测试的同学可能想要打人了。

使用异常代替返回错误码

这样就可以将Try/catch代码块抽离出来,因为Try/catch代码块影响了正常程序的流程,看起来很丑陋。

函数的主要规则就是这些,那么如何才能写出这样的函数呢?其实没有什么特别的技巧,就是记住这些规则,在每次写完代码之后再斟酌一番,对代码进行反复的打磨,修改不合适的命名,抽离冗长的函数。久而久之,你的代码一定会被人称赞的。

说完函数再来说一下如何写好一个类。

还是短小

没错,类也应该短小,不过这里短小的定义和函数短小的定义稍有不同,我们通常以“权责”来衡量。先看下面这个类。

1
2
3
4
5
6
7
public class SuperDashboard extends JFrame implements MetaDataUser {
public Component getLastFocusedComponent()
public void setLastFocused(Component lastFocused)
public int getMajorVersionNumber()
public int getMinorVersionNumber()
public int getBuildNumber()
}

这个类只提供了5个方法,应该不算长,但是我要说,它仍然不满足我们“短小”的条件,原因就是违反了单一权责原则。单一权责指的是一个类只描述一类事。上面这个类有对最后焦点组件的读写方法,还有获取版本号和序列号的方法。只要我们描述一个类时,用到了类似于“还有”这样的字眼时,那么这个类就违反了单一权责原则,就需要对其中的方法进行抽离。

为了修改而组织

大多数系统都会进行持续的迭代,而这也意味着我们需要不断对代码进行修改。而修改代码往往伴随着风险。所以,我们需要做的就是,修改一个方法时,不对其他方法造成影响。当我们开始修改时,就要评估好影响,然后将方法进行抽象,拆分。力求做到每次修改都不影响其他类(即降低耦合)。

对于写好一个类,总结来说就是“高内聚,低耦合”。

Jackey Wang wechat
欢迎关注我的公众号,一起讨论如何写bug
-------------本文结束感谢您的阅读-------------
原创不易,感谢支持