代码整洁之道(二)
第三章 函数
(3.1)短小
这也意味着函数不应该大到足以容纳嵌套结构。所以,函数的缩进层级不该多于一层或两层,便于阅读和理解;
(3.2)只做一件事情
函数应该做一件事。做好这件事,只做这一件事;
<3.2.1>如果函数只是做了该函数名下同一抽象层上的步骤,则函数还是只做了一件事情;
<3.2.2>只做一件事的函数无法合理的被切分为多个区段;
(3.3)每个函数一个抽象层级
<3.3.1>要确保函数只做一件事,函数中的语句都要在同一抽象层级上;
<3.3.2>自顶向下读代码:向下规则
我们想要代码拥有自顶向下的阅读顺序。我们想要让每个函数后面都跟着位于下一层级的函数;
(3.4)switch语句(*,这一点自己缺乏深入理解啊)
我们总无法避免switch语句,不过还是能够确保每个switch都埋藏在较低的抽象层次,而且永远不重复
(3.5)使用描述性的名称
(3.6)函数参数
最理想的参数数量是0,其次是1,再次是2,应尽量避免3参数函数;
输出参数比输入参数还要难以理解;
<3.6.1>一元函数的普遍形式
<3.6.1.1>寻求关于那个参数的问题:Boolean fileExists(“MyFile”)
<3.6.1.2>操作该参数,将其转换为其他什么东西再输出
(还有一种虽不那么普遍但仍级有用的单参数的函数形式,那就是事件,往往有输入没有输出,程序将函数看做是一个事件,使用该参数修改系统状态)
<3.6.2>标示参数
标示参数丑陋不堪,向函数传入布尔值简直就是骇人听闻的说法。这样做,方法签名立即变得复杂起来,大声宣布本函数不只做一件事情;
<3.6.3>二元函数
有两个参数的函数要比一个参数的函数难懂。当然了,有些时候两个参数正好,例如说:传入一个点的两个坐标值;
<3.6.4>三元函数
有三元函数的函数比二元函数的理解难懂的多;
<3.6.5>参数对象
如果函数看起来有两个,三个或者三个以上的参数,那么其中的一些参数要考虑封装成类;
<3.6.6>参数列表
有时,我们想要向函数传递数量可变的参数。例如:String.Format()
同理,这列函数中,参数个数多了也有可能犯错;
<3.6.7>动词与关键词
给函数起个好名字,能够较好的解释函数的意图以及参数的顺序和意图;
(3.7)无副作用
副作用是一种谎言。函数承诺只做一件事情,但是会做其他被藏起来的事情。有时,他会对自己类中的变量做出未能预期的改动。有时,他会把变量搞成了向函数传递的参数或是系统全局变量。无论哪种情况,都是具有破坏性的,会导致古怪的时序性耦合和顺序依赖;
备注 : 参数大多会被自然而然的看作是输入。普遍而言,应当避免使用输出参数,如果参数必须要修改某种状态,就修改所属对象的状态吧。
(3.8)分割指令与询问
函数要么做什么事,要回答什么事,但二者不可兼得。函数应该修改某对象的状态或是返回该对象的有关信息,两样都干常会导致混乱。
(3.9)使用异常替代返回错误码
使用错误码的缺点:
<a>轻微违反了指令与询问分割的规则,他鼓励了在if判断中,把指令当做表达式使用;
<b>会导致深层次的嵌套结构,且错误码返回时,就是在要求调用者立即处理错误
使用异常的好处:
使用异常替代错误码,就可以将错误处理从主代码路径中抽离出来得到简化;
<3.9.1>抽离Try/Catch块
Try/Catch块丑陋不堪,他们弄乱了代码结构,把错误处理和正常流程混为一谈,最好把Try和catch块的代码主体部分抽离出来,分别形成函数;
<3.9.2>错误处理就是一件事
函数应该只做一件事,而错误处理就是一件事。如果try在某个函数中存在,它就该是这个函数的第一个单词,而且在catch/finally代码块的后面不该有其他内容;
<3.9.3>Error.java依赖磁铁
返回错误码通常暗示某处有个类或者枚举定义了所有的错误码,在多处会有对该类或者枚举的依赖,改动了这个类或者枚举需要重新编译或者部署;如果使用异常的话,新异常就可以从原来的异常基础上派生出来,无需重新编译或者部署;
(3.10)别重复自己
重复会导致问题,代码因此变得臃肿,也会导致改动犯错等。重复可能是软件中一切邪恶的根源。
(3.11)结构化编程(了解即可,主要针对大函数适用)
(3.12)如何写出这样的函数
初稿也许粗陋无序,你就斟酌推敲,直至达到你心目中的样子。我并不一开始就按照这些规则来写,我相信也没有人可以达到;
(3.13)小结
大师级别的程序员把系统当做故事来讲,而不是当做程序来写。你编写的函数必须干净利落的拼装在一起,形成一种精确而清晰的语言,帮助你讲故事。