敏捷的编码

敏捷编码

代码要清晰地表达意图

开发代码时,应该更注重可读性,而不是只图本身方便。代码阅读的次数要远远超过编写的次数,因此在编写的时候值得花点功夫让它读起来更加简单。java

PIE原则(Program Intently and Expressively)
代码必须明确说出你的意图,并且必须富有表达力。这样可让代码更易于被别人阅读和理解。代码不让人迷惑,也就减小了发生潜在错误的可能。一言以蔽之,代码应意图清晰,表达明确。程序员

在编写代码时,应该使用语言特性来提高表现力。使用方法名来传达意向,对方法参数的命名要帮助读者理解背后的想法。异常传达的信息是哪些可能会出问题,以及如何进行防护式编程,要正确地使用和命名异常。好的编码规范可让代码变得易于理解,同时减小没必要要的注释和文档。编程

具体技巧

  • 如今对你显而易见的事情,对别人可能并不是如此,对于一年之后的你来讲,也不必定显而易见。

用代码沟通

一般程序员都很讨厌写文档,这是由于大部分文档都与代码没有关系,而且愈来愈难以保证其符合目前的最新情况。这不仅违反了DRY原则(Don't Repeat Yourself),还会产生令人误解的文档,这还不如没有文档。工具

创建代码文档无外乎两种方式:利用代码自己;利用注释来沟通代码以外的问题。性能

Don't comment to cover up.
若是必须通读一个方法的代码才能了解它作了什么,那么开发人员先要投入大量的时间和精力才能使用它。反过来说,只需短短几行注释说明方法行为,就可让生活变得轻松许多。开发人员能够很快了解到它的意图、它的期待结果,以及应该注意之处——这可省了你很多劲儿。单元测试

应该文档化你全部的代码吗?在某种程度上说,是的。但这并不意味着要注释绝大部分代码,特别是在方法体内部。源代码能够被读懂,不是由于其中的注释,而应该是因为它自己优雅而清晰——变量名运用正确、空格使用获得、逻辑分离清晰,以及表达式很是简洁。测试

如何命名很重要。程序元素的命名是代码读者必读的部分。经过使用细心挑选的名称,能够向阅读者传递大量的意图和信息。反过来说,使用人造的命名范式(好比如今已经无人问津的匈牙利表示法)会让代码难以阅读和理解。这些范式中包括的底层数据类型信息,会硬变吗在变量名和方法名中,造成脆弱、僵化的代码,并会在未来形成麻烦。使用细心挑选的名称和清晰的执行路径,代码几乎不须要注释。优化

对于显而易见的代码增长注释,也会有一样的问题。这种注释很常见——一般是由过于热心的IDE插入的。许多注释没有传递任何有意义的信息,这种注释只会分散注意力,并且很容易失去时效性。编码

对于类中的每一个方法可能要说明下列信息:设计

  • 目的:为何须要这个方法?
  • 需求(前置条件):方法须要什么样的输入,对象必须处于何种状态,才能让这方法工做?
  • 承诺(后置条件):方法成功执行后,对象如今处于什么状态,有哪些返回值?
  • 异常:可能会发生什么样的问题?会抛出什么样的异常?

要感谢如RDoc、javadoc和ndoc这样的工具,使用它们能够很方便地直接从代码注释建立有用的、格式优美的文档。这些工具抽取注释,并生成样式漂亮且带有超连接的HTML输出。

具体技巧

  • 在代码能够传递意图的地方不要使用注释
  • 解释代码作了什么的注释用处不那么大。相反,注释要说明为何会这样写代码。
  • 当重写方法时,保留描述原有方法意图和约束的注释。

动态评估取舍

对任何单个因素如此专断地强调,而不考虑它是不是项目成功的必要因素,必然致使灾难的发生。若是应用的性能已经足够好了,还有必要继续投入精力让其运行的更快一点吗?大概不用了吧。一个应用还有许多其余方面的因素一样重要。与其花费时间去提高千分之一的性能表现,也许减小开发投入,下降成本,并尽快让应用程序上市销售更有价值。

问题的关键是要多长个心眼儿,而不是总按照习惯的思路去解决问题。若是团队认为性能上还有提高的空间,那么就去咨询一下利益相关者,让他们决定应将重点放在哪里。没有适宜全部情况的最佳解决方案。你必须对手上的问题进行评估,并选出最合适的解决方案。每一个设计都是针对特定问题的一一只有明确地进行评估和权衡,才能获得更好的解决方案。

具体技巧

  • 若是如今投入额外的资源和精力,是为了未来可能获得的好处,要确认投入必定要获得回报(大部分状况下,是不会有回报的)
  • 真正的高性能系统,从一开始设计时就在向这个方向努力。
  • 过早的优化是万恶之源。
  • 过去用过的解决方案对当前的问题可能适用,也可能不适用。不要事先预设结论,先看看如今是什么情况。

增量式编程

若是不对本身编写的代码进行测试,保证没有问题,就不要连续几个小时,甚至连续几分钟进行编程。相反,应该采用增量式的编程方式。增量式编程能够精炼并结构化你的代码。代码被复杂化、变成一团乱麻的概率减小了。所开发的代码基于即时的反馈,这些反馈来自以小步幅方式编写代码和测试的过程。

采起增量式编程和测试,会倾向于建立更小的方法和更具内聚性的类。你不是在埋头盲目地编写一大堆代码。相反,你会常常评估代码质量,并不时地进行许多小调整,而不是一次修改许多东西。

在编写代码的时候,要常常留心能够改进的微小方面。这可能会改善代码的可读性。也许你会发现能够把一个方法拆成几个更小的方法,使其变得更易于测试。在重构的原则指导下,能够作出许多细微改善(《重构:改善既有代码的设计》)。可使用测试优先开发方式,做为强制进行增量式编程的方式。关键在于持续作一些细小而有用的事情,而不是作一段长时间的编程或重构。

这就是敏捷的方式。

具体技巧

  • 若是构建和测试循环话费的时间过长,你就不会但愿常常运行它们了。要保证测试能够快速运行。
  • 在编译和测试运行中,停下来想想,并暂时远离代码细节,这是保证不会偏离正确方向的好办法。
  • 要休息的话就要好好休息,请远离键盘。
  • 要像重构你的代码那样,重构你的测试,并且要常常重构测试。

保持简单

不要让本身被迫进行过度设计,也不要将代码过度复杂化。

Simple is not simplistic.
简单这个词汇被人们大大误解了(在软件开发工做以及人们的平常生活中,皆是如此)。它并不意味着简陋、业余或是能力不足。
优雅的代码第一眼看上去,就知道它的用处,并且很简洁。可是这样的解决方案不是那么容易想出来的。这就是说,优雅说易于理解和辨识的,可是要想建立出来就困可贵多了。

除非有不可辩驳的缘由,不然不要使用模式、原则和高难度技术之类的东西。

具体技巧

  • 代码几乎老是能够获得进一步精炼,可是到了某个点以后,再作改进就不会带来任何实质性的好处了。这时开发人员就该停下来,去作其余方面的工做了。
  • 要将目标牢记在心:简单、可读性高的代码。强行让代码变得优雅与过早优化相似,一样会产生恶劣的影响。
  • 固然,简单的解决方案必需要知足功能需求。
  • 泰国简洁不等于简单,那样没法达到沟通的目的。
  • 一我的认为简单的东西,可能对另外一我的就意味着复杂。

编写内聚的代码

内聚性用来评估一个组件(包、模块或配件)中成员的功能性。内聚程度高,代表各个成员共同完成了一个功能特性或是一组功能特性。内聚程度低的话,代表各个成员提供的功能是互不相干的。

如何组织一个组件中的代码,会对开发人员的生产力和所有代码的可维护性产生重要影响。在决定建立一个类的时候,问问本身,这个类的功能是否是与组件中其余某个类的功能相似,并且功能紧密相关。这就是组件级的内聚性。

类也要遵循内聚性。若是一个类的方法和属性共同完成了一个功能或是一系列紧密相关的功能,这个类就是内聚的。

一些设计技巧能够起到帮助做用。举例来讲,咱们经常使用模型-视图-控制器(MVC)模式来分离展现层逻辑、控制器和模型。这个模式很是有效,由于它可让开发人员得到更高的内聚性。模型中的类包含一种功能,在控制器中的类包含另外的功能,而视图中的类则只关心UI。

内聚性会影响一个组件的可重用性。

具体技巧

  • 有可能会把一些东西拆分红不少微小的部分,而使其失去了实用价值。当你须要一只袜子的时候,一盒棉线不能带给你任何帮助。
  • 具备良好内聚性的代码,可能会根据需求的变化,而成比例地进行变动。考虑一下,实现一个简单的功能变化须要变动多少代码。

告知,不要询问

“面向过程的代码取得信息,而后作出决策;面向对象的代码让别的对象去作事情。”

做为某段代码的调用者,开发人员绝对不该该基于被调用对象的状态来作出任何决策,更不能去改变该对象的状态。这样的逻辑应该是被调用对象的责任,而不是你的。在该对象以外替它作决策,就违反了它的封装原则,并且为bug提供了滋生的土壤。

将命令与查询分离开来
Keep commands separate from queries.
一个常规的命令可能会改变对象的状态,并且有可能反馈一些有用的值,以方便使用。
一个查询仅仅提供给开发人员对象的状态,并不会对其外部的可见状态进行修改。

像命令这种会产生内部影响的方法,强化了告知,不要询问的建议。此外,保证查询没有反作用,也是很好的编码实践,由于开发人员能够在单元测试中自由使用它们,在断言或者调试器中调用它们,而不会改变应用的状态。

具体技巧

  • 一个对象若是只是用做大量数据容器,这样的作法很可疑。有些状况下会须要这样的东西,但并不像想象的那么频繁。
  • 一个命令返回数据以方便使用时没有问题的(若是须要的话,建立单独读取数据的方法也是能够的)。
  • 绝对不容许一个看起来无辜的查询去修改对象的状态。

根据契约进行替换

保持系统灵活性的关键方式,是当新代码取代原有代码以后,其余已有的代码不会意识到任何差异。

Liskov替换原则告诉咱们:任何继承后获得的派生类对象,必须能够替换任何被使用的基类对象,并且使用者没必要知道任何差别。要遵照Liskov替换原则,相对基类的对应方法,派生类方法应该不要求更多,不承诺更少;要能够进行自由的替换。在设计类的继承层次时,这是一个很是重要的考虑因素。

若是违反了Liskov替换原则,继承层次可能仍然能够提供代码的可重用性,可是将会失去可扩展性。类继承关系的使用者如今必需要检查给定对象的类型,以肯定如何针对其进行处理。当引入新的类以后,调用代码必须常常评估并修正。这不是敏捷的方式。

针对is-a关系使用继承;针对has-a或uses-a关系使用委托。
聚合时指在类中包含一个对象,而且该对象是其余类的实例,开发人员将责任委托给所包含的对象来完成。

具体技巧

  • 相对继承来讲,委托更加灵活,适应力也更强。
  • 继承不是魔鬼,只是长久以来被你们误解了。
  • 若是你不肯定一个接口作出了什么样的承诺,或是有什么样的需求,那就很难提供一个对其有意义的实现了。
相关文章
相关标签/搜索