【前端阅读】——《代码整洁之道》摘记之整洁代码、命名、函数、注释

【前端阅读】——《代码整洁之道》摘记之整洁代码、命名、函数、注释

这本书提出一种观念:代码质量与其整洁度成正比。干净的代码,既在质量上较为可靠,也为后期维护、升级奠定了良好基础。(作者认为书可以有另一个名字:《如何在意代码》)

读这本书,促使我思考代码中何谓正确,何谓错误。更重要的是,它还可以促使自己重新评估自己的专业价值观,以及对自己技艺的承诺。

1、整洁编程

  • 混乱风险:制造混乱无助于赶上期限。混乱只会立刻拖慢你,叫你错过期限,赶上期限的唯一方法——做得快的唯一方法——就是始终尽可能的保持代码整洁。
  • 代码感:写整洁代码,需要遵循大量的小技巧,贯彻刻苦习得的“整洁感”。这种“代码感”就是关键所在。缺乏“代码感”的程序员,看混乱是混乱,无处着手。有“代码感”的程序员能从混乱中看出其他的可能与变化。“代码感”帮助程序员选出最好的方案,并指导程序员制订修改行动计划,按图索骥。
  • “整洁的代码只做好一件事”——Bjarne Stroustrup(C++语言发明者)。软件设计的许多原则最终都会归结为这句警语。糟糕的代码想做太多事,它意图混乱、目的含混。整洁的代码力求集中。每个函数、每个类和每个模块都全神贯注于一事,完全不受四周细节的干扰和污染。

    整洁的代码应可由作者之外的开发者阅读和增补。它应当有单元测试和验收测试。它使用有意义的命名。它只提供一种而非多种做一件事的途径。它只有尽量少的依赖关系,而且要明确地定义和提供清晰、尽量少的API。代码应通过其字面表达含义,因为不同的语言导致并非所有必需信息均可通过代码自身清晰表达。——Dave Thomes(OTI公司创始人,Eclipse战略教父)

  • 贝克的简单代码规则:消除重复并提高表达力,提早构建简单抽象
  1. 能通过所有测试
  2. 没有重复代码
  3. 体现系统中的全部设计理念
  4. 包括尽量少的实体,比如类、方法、函数等

 

2、有意义的命名

  • 名副其实
  • 避免误导
  • 做有意义的区分
  • 使用读的出来的名称
  • 使用可搜索的名称
  1. 单字母名称用于短方法中的本地变量。名称长短应与其作用域大小相对应。若变量或常量可能在代码中多处使用,则应赋其以便于搜索的名称。
  • 避免使用编码
  1. 不必用m_前缀来标明成员变量。应当把类和函数做的足够小,消除对成员前缀的需要。(人们会很快学会无视前缀或后缀,只看到名称中有意义的部分。代码读的越多,眼中就越没有前缀。最终,前缀变作了不入法眼的废料,变作了旧代码的标志物)
  • 避免思维映射
  1. 单字母变量名就是个问题。在多数除循环计数器之外的其他情况下,单字母名称不是个好选择,读者必须在脑中将它映射为真实概念。(仅仅是因为有了a和b,就要取名为c,实在并非像样的理由。)
  2. 聪明程序员和专业程序员之间的区别在于:专业程序员了解,明确是王道。专业程序员善用其能,编写其他人能理解的代码。
  • 类名
  1. 类名和对象名应该是名词或名词短语,如Customer、WikiPage、Account和AddressParser
  2. 避免使用Mannager、Processor、Data、或Info这样的类名。
  • 方法名
  1. 方法名应当是动词或动词短语,如postPayment、deletePage或save
  2. 属性访问器、修改器和断言应该根据其值命名,并依Javabean标准加上get、set和is前缀。
    string name = employee.getname();
    customer.setName("mike");
    if (paycheck.isPosted())...
  • 别扮可爱
  1. 言到意到,意到言到。
  • 每个概念对应一个词
  1. 给每个抽象概念选一个词,并且一以贯之。(对于那些会用到你代码的程序员,一以贯之的命名法简直就是天降福音)
  • 别用双关语
  1. 避免将同一单词用于不同目的。同一术语用于不同概念,基本上就是双关语了。
  2. 比如,在多个类中都有add方法,该方法通过增加或连接两个现存值来获得新值。假设要写个新类,该类中有一个方法,把单个参数放到群集(collection)中。如果把这个方法叫做add,貌似和其他add方法保持了一致,但实际上语义却不同,应该用insert或append之类词来命名才对。(把该方法命名为add,就是双关语了)
  • 使用解决方案领域名称
  1. 记住,只有程序员才会读你的代码。所以,尽管去用那些计算机科学术语、算法名、模式名、数学术语吧
  2. 比如,对于熟悉访问者(VISITOR)模式的程序员来说,名称AccountVisitor富有意义。(程序员要做太多技术性工作,给这些事取个技术性的名称,通常是最靠谱的做法)
  • 使用源自所涉问题领域的名称
  1. 如果不能用程序员熟悉的术语来给手头的工作命名,就采用从所涉问题领域而来的名称吧。至少,负责维护代码的程序员就能去请教领域专家了。
  2. 优秀的程序员和设计师:其工作之一就是分离解决方案领域和问题领域的概念。与所涉问题领域更为贴近的代码,应当采用源自问题领域的名称。
  • 添加有意义的语境
  1. 很少有名称是能自我说明的——多数都不能。反之,你需要用到良好命名的类、函数或名称空间来放置名称,给读者提供语境。如果没这么做,给名称添加前缀就是最后一招了。
  2. 比如,对孤零零的一个state变量来说,可以添加前缀addrFirstName、addrLastName、addrState等,以此提供语境。至少,读者会明白这些变量是某个更大结构的一部分。当然,更好的方案是创建名为Address的类。这样,即便是编译器也会知道这些变量隶属某个更大的概念了。
  3. 语境的增强,也让算法能够通过分解为更小的函数而变得更为干净利落。
  4. //语境不明确的变量
    private void printGuessStatistics(char candidate,int count){
          String number;
          String verb;
          String pluralModifier;
           ...
    }
    
    //有语境的变量
    //创建GuessStaticsMessage类,把三个变量做成该类的成员字段
    public class GuessStaticsMessage{
          String number; 
          String verb; 
          String pluralModifier; 
           ... 
    }
  • 不要添加没用的语境
  1. 设若有一个名为“加油站豪华版”(Gas Station Deluxe)的应用,在其中给每个类添加GSD前缀就不是什么好点子。
  2. 只要短命称足够清楚,就要比长名称要好。
  3. 对于Address类的实体来说,accountAddress和customerAddress都是不错的名称,不过用在类名上就不太好了。Address是个好类名。如果需要与MAC地址、端口地址和Web地址相区别,我会考虑使用PostalAddress、MAC和URI。这样的名称更为准确,而精确正是命名的要点

 

3、函数

  •  短小
  1. 代码块和缩进:if语句、else语句、while语句等,其中的代码块应该只有一行。该行大抵应该是一个函数调用语句。这样不但能保持函数短小,而且,因为块内调用的函数拥有较具说明性的名称,从而增加了文档上的价值。
  2. 这也意味着函数不应该大到足以容纳嵌套结构。所以,函数的缩进层不该多于一层或两层。(这样的函数易于阅读和理解)
  • 只做一件事
  1. 函数应该做一件事。做好这件事。只做这一件事。
  2. 问题在于很难知道那件该做的事是什么?(其实,有时候一件事也很容易被看作是三件事或很多具体细化的步骤)如果函数只是做了该函数名下同一抽象层上的步骤,则函数还是只做了一件事
  3. 要判断函数是否不止做了一件事,还有一个方法,就是看是否能再拆出一个函数,该函数不仅只是单纯地重新诠释其实现。
  4. 函数中的区段:只做一件事的函数无法被合理的切分为多个区段。(这也是函数做事太多的明显征兆)
  • 每个函数一个抽象层级
  1. 函数中混杂不同抽象层级,往往让人迷惑。读者可能无法判断某个表达式是基础概念还是细节。更恶劣的是,就像破损的窗户,一旦细节与基础概念混杂,更多的细节就会在函数中纠结起来。
  2. 自项向下读代码:向下规则。这是保持函数短小、确保只做一件事的要诀。让代码读起来像是一系列自项向下的TO起头段落是保持抽象层级协调一致的有效技巧。

          

  • switch语句
  1. 问题:写出短小的switch语句很难(包括if/else在内),写出只做一件事的switch语句也很难,Switch天生要做N件事。
  2. 解决:利用多态,确保每个switch都埋藏在较低的抽象层级,而且永远不重复。
  3. 如下代码:将switch语句埋到抽象工厂底下,不让任何人看到。该工厂使用switch语句为Employee的派生物创建适当的实体,而不同的函数,如calculatePay、isPayday和deliverPay等,则藉由Emplyee接口多态地接受派遣。
  4. 对于switch语句,(作者的)规矩是如果只出现一次,用于创建多态对象,而且隐藏在某个继承关系中,在系统其他部分看不到,就还能容忍。当然也要就是论事,有时也会部分或全部违反这条规矩。

        

  • 使用描述性的名称
  1. 沃德原则:“如果每个例程都让你感到深合己意,那就是整洁代码。”函数越短小、功能越集中,就越便于取个好名字。
  2. 别害怕长名称。长而具有描述性的名称,要比短而令人费解的名称好。长而具有描述性的名称,要比描述性的长注释好。
  3. 选择描述性的名称能理清你关于模块的设计思路,并帮你改进之。追索好名称,往往导致对代码的改善重构。
  4. 命名方式要保持一致。使用与模块名一脉相承的短语、名词和动词给函数命名。例如:includeSTeardownPages、includeSetuoPages、includeSuiteSetupPage等
  • 函数参数
  1. 最理想的参数数量是零(零参数函数),其次是一(单参数函数),再次是二(双参数函数),应尽量避免三(三参数函数)。有足够特殊的理由才能用三个以上参数(多参数函数)——所以无论如何也不要这样做。
  2. 阅读模块所讲述的故事时,includeSetupPage()要比includeSetupPageInto(newPage-Content)易于理解。参数与函数名处在不同的抽象层级,它要求了解目前不是特别重要的细节(即那个Stringbuffer
  3. 测试的角度:要编写能确保参数的各种组合运行正常的测试用例,是一件非常困难的事情。
  4. 输出参数比输入参数还要难以理解。
  • 无副作用
  1. 副作用是一种谎言。函数承诺只做一件事,但还是会做其他被藏起来的事。有时,它会对自己类中的变量做出未能预期的改动。有时,它会对自己类中的变量做出未能预期的改动。有时,它会把变量搞成向函数传递的参数或是系统全局变量。无论哪种情况,都是具有破坏性的,会导致古怪的时序性耦合顺序依赖。(如果一定要时序性耦合,就应该在函数名称里说明)
  2. 输出参数:参数多数会被自然而然地看作是函数的输入。普遍而言,应避免使用输出参数。如果函数必须要修改某种状态,就修改所属对象的状态吧。
  • 分隔指令与询问
  1. 函数要么做什么事,要么回答什么事,但二者不可得兼。
  2. 函数应该修改某对象的状态,或是返回该对象的有关信息。两样都干常会导致混乱。
  • 使用异常替代返回错误码
  1. 从指令式函数返回错误码轻微违反了指令与询问分隔的规则。它鼓励了在if语句判断中把指令当作表达式使用。另一方面,如果使用异常替代返回错误码,错误处理代码就能从主路径代码中分离出来,得到简化。
  2. 抽离Try/Catch代码块:Try/Catch代码块丑陋不堪。它们搞乱了代码结构,把错误处理与正常流程混为一谈。最好把它的主体部分抽离出来,另外形成函数。如图:
  3. 错误处理就是一件事:函数应该只做一件事,错误处理就是一件事,因此,处理错误的函数不该做其他事。

       

  • 如何写出这样的函数
  1. 一开始都冗长而复杂。然后,会打磨,分解函数、修改名称、消除重复。缩短和重新安置方法。有时还拆散类。同时保持测试通过。
  2. 并不从一开始就按照规则写函数。一般没人做得到。

 

4、注释

  • 注释会撒谎。
  1. 注释存在的越久,就离其所描述的代码越远,越来越变得全然错误。(原因很简单,程序员不能坚持维护注释)
  • 注释不能美化糟糕的代码
  1. 带有少量注释的整洁而有表达力的代码,要比带有大量注释的零碎而复杂的代码像样的多。
  • 用代码来阐述
  1. 很多时候,简单到只需要创建一个描述与注释所言同一事物的函数即可。
  • 好注释——唯一真正好的注释是你想办法不去写的注释
  1. 法律信息
  2. 提供信息的注释
  3. 对意图的解释
  4. 阐释
  5. 警示
  6. TODO注释(一种程序员认为应该做,但由于某些原因目前还没做的工作)
  7. 放大
  8. 公共API中的Javadoc
  • 坏注释
  1. 喃喃自语
  2. 多余的注释
  3. 误导性注释
  4. 循规式注释
  5. 日志性注释
  6. 废话注释
  7. 可怕的废话
  8. 能用函数或变量时就别用注释
  9. 位置标记
  10. 括号后面的注释(尽管这对于含有深度嵌套结构的长函数可能有意义,但只会给我们更愿意编写的短小、封装的函数带来混乱。如果你发现自己想标记右括号,其实应该做的是缩短函数)
  11. 归属与署名
  12. 注释掉的代码
  13. HTML注释
  14. 非本地信息
  15. 信息过多
  16. 不明显的联系
  17. 函数头
  18. 非公共代码中的Javadoc

 

注:转载请注明出处

posted @ 2017-10-28 23:24 柳洁琼Elena 阅读( ...) 评论( ...) 编辑 收藏
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值