目录
一、有意义的命名
1.名副其实、见名知意
2.避免误导
(1)应当避免使用与本意相悖的词
(2)提防使用不同之处较小的名称
3.做有意义的区分
4.使用读得出来的名称
5.使用可搜索的名称
6.避免使用编码
(1)匈牙利语标记法
(2)成员前缀
(3)接口和实现
7.避免思维映射
8.类名
9.方法名
10.每个概念对应一个词
11.别用双关语
12.使用解决方案领域名称
13.使用源自所涉问题领域的名称
14.添加有意义的语境
15.不要添加没用的语境
16.最后的话
二、函数
1.短小
2.只做一件事
3.每个函数一个抽象层级
(1)确保函数只做一件事
(2)自顶向下读代码:向下规则
4.switch 语句
5.使用描述性的名称
(1)别害怕长名称
(2)别害怕花时间取名字
(3)命名方式要保持一致
6.函数参数
7.一元函数的普遍形式
8.标识参数
9.二元函数
10.三元函数
11.参数对象
12.参数列表
13.动词与关键字
14.无副作用
15.分隔指令与询问
16.使用异常替代返回错误码
(1)抽离 Try/Catch 代码块
(2)错误处理就是一件事。
(3)Error.java 依赖磁铁
17.别重复自己
18.结构化编程
19.如何写出这样的函数
20.小结
一、有意义的命名
1.名副其实、见名知意
(1)变量名不要太随意,haha、list1、ok、theList 这些都没啥意义。
(2)如果名称需要注释来补充,那就不算是名副其实。
2.避免误导
(1)应当避免使用与本意相悖的词
包含List、import、java等类名、关键字或特殊字;字母o与数字0,字母l与数字1等。
(2)提防使用不同之处较小的名称
比如:XYZControllerForEfficientHandlingOfStringsXYZControllerForEfficientStorageOfStrings。
3.做有意义的区分
反面教材,变量名:a1、a2、a3。
避免冗余,不要出现Variable、表字段中避免出现table、字符串避免出现nameString,直接name就行,知道是字符串类型。
再比如:定义了两个类:Customer类和CustomerObject类,如何区分?
定义了三个方法:getActiveAccount()、getActiveAccounts()、getActiveAccountInfo(),如何区分?
(1)如果名称必须相异,那其意思也应该不同才对。
(2)要区分名称,就要以读者能鉴别不同之处的方式来区分。
4.使用读得出来的名称
不要使用自己拼凑出来的单词,比如:xsxm(学生姓名);genymdhms(生成日期,年、月、日、时、分、秒)。
所谓的驼峰命名法,尽量使用完整的单词。如果名称读不出来,讨论的时候就会像个傻鸟。
这不是小事,因为编程本就是一种社会活动。命名应当为恰当的英语词,而非傻乎乎的自造词。
5.使用可搜索的名称
一些常量,最好不直接使用数字,而指定一个变量名,这个变量名可以便于搜索到。比如:找MAX_CLASSES_PER_STUDENT很容易,但想找数字7就麻烦了。
长名称胜于短名称,搜得到的名称胜于用自造编码代写就的名称。
若变量或常量可能在代码中多处使用,则应赋其以便于搜索的名称。
6.避免使用编码
(1)匈牙利语标记法
(2)成员前缀
(3)接口和实现
作者不喜欢把接口使用大写字母I来开头,实现也希望只是在后面添加Imp。
编码已经太多,无谓再自找麻烦。把类型或作用域编进名称里面,徒然增加了解码的负担。带编码的名称通常也不便发音,容易打错。
7.避免思维映射
不应当让读者在脑中把你的名称翻译为他们熟知的名称。
比如传统上惯用单字母名称做循环计数器。所以就不要给一些非计数器的变量命名为:i、j、k等。
8.类名
类名与对象名应该是名词与名词短语。
如Customer、WikiPage、Account和AddressParser。避免使用Data或Info这样的类名。
不能使动词。比如:Manage、Process
9.方法名
10.每个概念对应一个词
项目中同时出现controllers与managers,为什么不统一使用其中一种?对于那些会用到你代码的程序员,一以贯之的命名法简直就是天降福音。
函数名称应当独一无二,而且要保持一致,这样你才能不借助多余的浏览就找到正确的方法。
11.别用双关语
12.使用解决方案领域名称
13.使用源自所涉问题领域的名称
如果不能用程序员熟悉的术语来给手头的工作命名,就采用从所涉问题领域而来的名称吧。
至少,负责维护代码的程序员就能去请教领域专家了。
优秀的程序员和设计师,其工作之一就是分离解决方案领域和问题领域的概念。与所涉问题领域更为贴近的代码,应当采用源自问题领域的名称。
14.添加有意义的语境
很少有名称是能自我说明的—多数都不能。反之,你需要用有良好命名的类、函数或名称空
间来放置名称,给读者提供语境,可以把相关的变量放到一个类中,使用这个类来表明语境。
如果没这么做,给名称添加前缀就是最后一招了。
15.不要添加没用的语境
名字中带有项目的缩写,这样完全没有必要。比如有一个名为“加油站豪华版”(Gas Station Deluxe)的项目,在其中给每个类添加GSD前缀就不是什么好策略。
只要短名称足够清楚,就要比长名称好,别给名称添加不必要的语境,命名的要点是精确。
16.最后的话
取好名字最难的地方在于需要良好的描述技巧和共有文化背景。与其说这是一种技术、商业或管理问题,还不如说是一种教学问题。其结果是,这个领域内的许多人都没能学会做得很好。我们有时会怕其他开发者反对重命名。如果讨论一下就知道,如果名称改得更好,那大家真的会感激你。多数时候我们并不记忆类名和方法名。我们使用现代工具对付这些细节,好让自己集中精力于把代码写得就像词句篇章、至少像是表和数据结构(词句并非总是呈现数据的**手段)。改名可能会让某人吃惊,就像你做到其他代码改善工作一样。别让这种事阻碍你的前进步伐。
不妨试试上面这些规则,看你的代码可读性是否有所提升。如果你是在维护别人写的代码,使用重构工具来解决问题。效果立竿见影,而且会持续下去。
二、函数
1.短小
函数的第一规则是要短小。第二条规则是还要更短小。我无法证明这个断言。我给不出任何证实了小函数更好的研究结果。经验告诉我,函数就该小。
每个函数都一目了然。每个函数都只说一件事。而且,每个函数都依序把你带到下一个函数。这就是函数应该达到的短小程度!
例如if 语句、else 语句、while 语句等,其中的代码块应该只有一行。该行大抵应该是一个函数调用语句。
这样不但能保持函数短小,而且,因为块内调用的函数拥有较具说明性的名称,从而增加了文档上的价值。
这也意味着函数不应该大到足以容纳嵌套结构。所以,函数的缩进层级不该多于一层或两层。
当然,这样的函数易于阅读和理解。
2.只做一件事
3.每个函数一个抽象层级
(1)确保函数只做一件事
要确保函数只做一件事,函数中的语句都要在同一抽象层级上。
函数中混杂不同抽象层级,往往让人迷惑。读者可能无法判断某个表达式是基础概念还是细节。
更恶劣的是,就像破损的窗户,一旦细节与基础概念混杂,更多的细节就会在函数中纠结起来。
(2)自顶向下读代码:向下规则
我们想要让代码拥有自顶向下的阅读顺序。我们想要让每个函数后面都跟着位于下一抽象层级的函数,这样一来,在查看函数列表时,就能偱抽象层级向下阅读了。我把这叫做向下规则。
换一种说法。我们想要这样读程序:程序就像是一系列 TO 起头的段落,每一段都描述当前抽象层级,并引用位于下一抽象层级的后续 TO 起头段落。
4.switch 语句
5.使用描述性的名称
(1)别害怕长名称
(2)别害怕花时间取名字
你当尝试不同的名称,实测其阅读效果。在 Eclipse 或 IntelliJ 等现代 IDE 中改名称易如反掌。
使用这些 IDE 测试不同名称,直至找到最具有描述性的那一个为止。
选择描述性的名称能理清你关于模块的设计思路,并帮你改进之。追索好名称,往往导致对代码的改善重构。
(3)命名方式要保持一致
6.函数参数
7.一元函数的普遍形式
向函数传入单个参数有两种极普遍的理由。
一种是是操作该参数,将其转换为其他什么东西,再输出之。
还有一种虽不那么普遍但仍极有用的单参数函数形式,那就是事件(event)。在这种形式中,
有输入参数而无输出参数。程序将函数看作是一个事件,使用该参数修改系统状态,例如 void passwordAttemptFailedNtimes(int attempts)。
小心使用这种形式。应该让读者很清楚地了解它是个事件。谨慎地选用名称和上下文语境。
8.标识参数
9.二元函数
有两个参数的函数要比一元函数难懂。当然,有些时候两个参数正好。
二元函数不算恶劣,而且你当然也会编写二元函数。不过,你得小心,使用二元函数要付出代价。你应该尽量利用一些机制将其转换成一元函数。
10.三元函数
另一方面,这里有个并不那么险恶的三元函数:assertEquals(1.0, amount, .001)。虽然也要费点神,还是值得的。得到“浮点值的等值是相对而言”的提示总是好的。
11.参数对象
如果函数看来需要两个、三个或三个以上参数,就说明其中一些参数应该封装为类了。
从参数创建对象,从而减少参数数量,看起来像是在作弊,但实则并非如此。当一组参数被共同传递,往往就是该有自己名称的某个概念的一部分。
12.参数列表
有可变参数的函数可能是一元、二元甚至三元。超过这个数量就可能要犯错了。
13.动词与关键字
给函数取个好名字,能较好地解释函数的意图,以及参数的顺序和意图。对于一元函数,函数和参数应当形成一种非常良好的动词/名词对形式。
把参数的名称编码成函数名,这大大减轻了记忆参数顺序的负担。
14.无副作用
15.分隔指令与询问
16.使用异常替代返回错误码
从指令式函数返回错误码轻微违反了指令与询问分隔的规则。它鼓励了在 if 语句判断中把指令当作表达式使用。
if (deletePage(page) == E_OK)这不会引起动词/形容词混淆,但却导致更深层次的嵌套结构。当返回错误码时,就是在要求调用者立刻处理错误。
另一方面,如果使用异常替代返回错误码,错误处理代码就能从主路径代码中分离出来,得到简化。
(1)抽离 Try/Catch 代码块
Try/catch 代码块丑陋不堪。它们搞乱了代码结构,把错误处理与正常流程混为一谈。最好把 try 和 catch 代码块的主体部分抽离出来,另外形成函数。
public void delete(Page page) {
try {
deletePageAndAllReferences(page);
}
catch (Exception e) {
logError(e);
} }
private void deletePageAndAllReferences(Page page) throws Exception {
deletePage(page);
registry.deleteReference(page.name);
configKeys.deleteKey(page.name.makeKey());
}
private void logError(Exception e) {
logger.log(e.getMessage());
}
在上例中,delete 函数只与错误处理有关。很容易理解然后就忽略掉。deletePageAndAllReference 函数只与完全删除一个 page 有关。
错误处理可以忽略掉。有了这样美妙的区隔,代码就更易于理解和修改了。
(2)错误处理就是一件事。
(3)Error.java 依赖磁铁
17.别重复自己
18.结构化编程
19.如何写出这样的函数
写代码和写别的东西很像。在写论文或文章时,你先想什么就写什么,然后再打磨它。初稿也许粗陋无序,你就斟酌推敲,直至达到你心目中的样子。我写函数时,一开始都冗长而复杂。有太多缩进和嵌套循环。有过长的参数列表。名称是随意取的,也会有重复的代码。不过我会配上一套单元测试,覆盖每行丑陋的代码。然后我打磨这些代码,分解函数、修改名称、消除重复。我缩短和重新安置方法。有时我还拆散类。同时保持测试通过。
最后,遵循以上列出的规则,我组装好这些函数。我并不从一开始就按照规则写函数。我想没人做得到。
20.小结
每个系统都是使用某种领域特定语言搭建,而这种语言是程序员设计来描述那个系统的。函数是语言的动词,类是名词。
这并非是退回到那种认为需求文档中的名词和动词就是系统中类和函数的最初设想的可怕的旧观念。其实这是个历史更久的真理。
编程艺术是且一直就是语言设计的艺术。大师级程序员把系统当作故事来讲,而不是当作程序来写。他们使用选定编程语言提供的工具构建一种更为丰富且更具表达力的语言,用来讲那个故事。
那种领域特定语言的一个部分,就是描述在系统中发生的各种行为的函数层级。
在一种狡猾的递归操作中,这些行为使用它们定义的与领域紧密相关的语言讲述自己那个小故事。
如果你遵循这些规则,函数就会短小,有个好名字,而且被很好地归置。
不过永远别忘记,真正的目标在于讲述系统的故事,而你编写的函数必须干净利落地拼装到一起,形成一种精确而清晰的语言,帮助你讲故事。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/34798.html