代码整洁之道——命名与函数
- 有意义的命名
做有意义的区分
- 不要以数字系列命名 没有提供导向作者意图的线索。如
public void copyChars(char a1[], char a2[])
如果将参数名改成source和destination,就会像样许多。
- 不要使用有相同意义的名字 如ProductInfo和ProductData类,很难区分两者的区别。
使用读得出来的可搜索的名称
- 尽量不要使用单字母名称和数字常量 如果要搜索一个数字或一个字母往往很难。长名字胜于短名字。
- 函数
短小
- 短小再短小 函数不应该有100行那么长,20行封顶最佳。
- 代码块和缩进 if语句、else语句、while语句等,其中的代码应该只有一行,就是一个函数调用语句。这样既保持了代码的短小,也使函数更易于阅读和理解。
只做一件事
- 如果函数只是做了该函数下的同一抽象层上的步骤,则函数只做了一件事
- 或者看函数能否再拆出一个函数,该函数是其下一抽象层的实现。
- 每个函数一个抽象层级 这是让代码读起来像是自顶向下的段落的有效方法。
函数参数
- 最理想的参数数量是0,越多越不好 从测试的角度看,参数的增加将使测试用例指数性增长。
- 一元函数的普遍形式 有两个极普遍的理由要向一个函数传单个参数。1,询问形式:想询问关于那个参数的问题,如boolean fileExits("Myfile")这样的; 2,转换形式:想要操作该参数,将其转换成什么,再输出之,如InputStream fileOpen("Myfile")这样的,把string类型的文件名转换为InputStream类型的返回值。
- 二元函数 二元函数普遍比一元函数难懂。有些情况下使用二元函数是更好的,如定义一个点Point p = new Point(0, 0)。但对于如assertEquals(expected, actual)这样的二元函数,因为它没有自然顺序所以经常会搞错两者的位置。因此,我们应该尽量利用一些机制将其转换成一元函数。例如,写成expected.assertEquals(actual)。
- 三个及以上参数 此时你应该想想其中一些参数是不是可以封装成类了。
- 函数的名字 要给函数其个好名字,能够较好地解释函数的意图,以及参数的顺序和意义。对于一元函数和其参数应当使用非常良好的动词/名词对的形式。如write(name)就相当令人认同。更好的名称是writeField(name),它可以告诉人们"name"是一个"field"。 此外,我们还可以利用函数的关键字形式,即把参数名称加入到函数名中,如assertEquals改成assertExpectedEqualsActual(expected, actual)会好一些,减轻了记忆的负担。
- 避免使用输出参数 如果函数要修改某种状态,应去修改所属对象的状态。
使用异常替代返回错误码
- 返回错误代码往往是在if语句中使用的,这可能会导致更深层次的嵌套结构。当返回错误代码是,就是要求调用这立刻处理错误。例如:
if (deletPage(page) == E_OK) { if (registry.deleteReference(page.name) == E_OK) { if (...) {} else{} } else { logger.log(); } }
另一方面,如果使用异常替代返回错误值,错误处理就能从主代码中分离出来。try{ ...... } catch (Exception e){ logger.log(); }
- 抽离try/catch代码块 try/catch代码往往会搞乱了代码结构,最好把它们从主体代码中抽离出来,另外形成函数。
public void delete(Page page) { try { deletePageAndAllReferences(page); } catch (Exception e) { logError(e); } } private void deletePageAndAllreference(Page page) throws Exception { deletePage(page); registry.deleteReference(page.name); } private void logError (Exception e) { logger.log(e.getMessage()); }
上例中,我们将try/catch代码块独立放到了delete函数中,然后又在try里调用正常流程的代码,在catch里调用处理错误的函数,这样美妙的区隔,delete函数就可以很容易理解并且忽略掉了,代码也更容易管理和修改。 - 错误处理就是一件事 错误处理函数不应该再做其他事,这就意味着,如果try在某个函数中存在,它就应该是该函数的第一个单词,而catch代码块后面也不该再有其他内容,即“前无古人,后无来者”。