高效程序员的十个习惯

高效程序员的十个习惯

来源于“高效程序员的45个习惯-敏捷开发修炼之道”中的部分习惯的摘抄与个人部分读后感,目的:注重于培养软件开发者的态度、原则、操守、价值观,交付高质量的软件

敏捷概述

迭代开发,价值优先分解任务,真实进度
站立会议,交流畅通用户参与,调整方向
结对编程,代码质量测试驱动,安全可靠
持续集成,尽早反馈自动部署,一键安装

1.对事不对人

对方案设计的讨论失控变成了情绪化的指责——做决定是基于谁提出了这个观点,而不是权衡观点本身的利弊。一个团队能够很公正地讨论一些方案的优点和缺点,你不会因为拒绝了有太多缺陷的方案而伤害别人,也不会因为采纳了某个不甚完美(但是更好的)解决方案而被人忌恨。

如果你准备提出一个想法,却担心有可能被嘲笑,或者你要提出一个建议,却担心自己丢面子,那么你就不会主动提出自己的建议了。然而,好的软件开发作品和好的软件设计,都需要大量的创造力和洞察力。分享并融合各种不同的想法和观点,远远胜於单个想法为项目带来的价值。
负面的评论和态度扼杀了创新,你必须把重点放在解决问题上,而不是去极力证明谁的主意更好。在团队中智商高是没有用的,如果他还很顽固并且拒绝合作,那就更糟糕。在这样的团队中,生产率和创新都会频临灭亡的边缘。
我们每个人都会有好的想法,也会有不对的想法,团队中的每个人都需要自由地表达观点。即使你的建议不被全盘接受,也能对终解决问题有所帮助。不要害怕受到批评。记住,任何一个专家都是从这里开始的。“你不需要很出色才能起步,但是你必须起步才能变得很出色。”
集体决策确实非常有效,但也有一些最好的创新源于很有见地的个人的独立思考。如果你是一个有远见的人,就一定要特别尊重别人的意见。你是一个掌舵者,一定要把握方向,深思熟虑,吸取各方的意见。另一个极端是缺乏生气的委员会,每个设计方案都需要全票通过。这样的委员会总是小题大作,如果让他们造一匹木马,很可能最后造出的是骆驼。并不是建议你限制会议决策,只是你不应该成为一意孤行的首席架构师的傀儡。这里建议你牢记亚里士多德的一句格言:“能容纳自己并不接受的想法,表明你的头脑足够有学识”

有效方式
设定终期限:如果你正在参加设计方案讨论会,或者是寻找解决方案时遇到问题,请设定一个明确的终期限,例如午饭时间或者一天的结束。这样的时间限制可以防止人们陷入无休止的理论争辩之中,保证团队工作的顺利进行。同时应现实一些:没有最好的答案,只有更合适的方案。设定期限能够帮你在为难的时候果断做出决策,让工作可以继续进行。
逆向思维:团队中的每个成员都应该意识到权衡的必要性。一种客观对待问题的办法是:先是积极地看到它的正面,然后再努力地从反面去认识它。目的是要找出优点多缺点少的那个方案,而这个好办法可以尽可能地发现其优缺点,这也有助于少带个人感情。
设立仲裁人:在会议的开始,选择一个仲裁人作为本次会议的决策者。每个人都要有机会针对问题畅所欲言。仲裁人的责任就是确保每个人都有发言的机会,并维持会议的正常进行。仲裁人可以防止明星员工操纵会议,并及时打断假大空式发言。如果你自己没有积极参与这次讨论活动,那么你好退一步做会议的监督者。仲裁人应该专注于调停,而不是发表自己的观点(理想情况下不应在整个项目中有既得利益)。当然,这项任务不需要严格的技术技能,需要的是和他人打交道的能力。
支持已经做出的决定:一旦方案被确定了(不管是什么样的方案),每个团队成员都必须通力合作,努力实现这个方案。每个人都要时刻记住,我们的目标是让项目成功
满足用户需求:客户并不关心这是谁的主意——他们关心的是,这个软件是否可以工作,并且是否符合他们的期望。
结果重要:设计充满了妥协,成功属于意识到这一点的团队。工作中不感情用事是需要克制力的,而你若能展现出成熟大度来,大家一定不会视而不见。这需要有人带头,身体力行,去感染另一部分人。

对事不对人:让我们骄傲的应该是解决了问题,而不是比较出谁的主意更好。

平衡的艺术
1.尽力贡献自己的好想法,如果你的想法没有被采纳也无需生气。不要因为只是想体现自己的想法而对拟定的好思路画蛇添足。
2. 脱离实际的反方观点会使争论变味。若对一个想法有成见,你很容易提出一堆不太可能发生或不太实际的情形去批驳它。这时,请先扪心自问:类似问题以前发生过吗?是否经常发生?
3. “我们不能采用这个方案,因为数据库厂商可能会倒闭”或者:用户绝对不会接受那个方案。你必须要评判那些场景发生的可能性有多大。想要支持或者反驳一个观点,有时候你必须先做一个原型或者调查出它有多少的同意者或者反对者。
4. 在开始寻找
好的解决方案之前,大家对“最好”的含义要先达成共识。在开发者眼中的 好,不一定就是用户认为 好的,反之亦然。
5. 只有更好,没有最好,只有在某个特定条件下更好的实践。
6. 不带个人情绪并不是要盲目地接受所有的观点。用合适的词和理由去解释为什么你不赞同这个观点或方案,并提出明确的问题。

2.跟踪变化

软件技术的变化如此之快,继续用你熟悉的语言做你的老本行,你不可能跟上技术变化的脚步。

跟上技术变化的步伐的建议
迭代和增量式的学习:每天计划用一段时间来学习新技术,它不需要很长时间,但需要经常进行。记下那些你想学习的东西——当你听到一些不熟悉的术语或者短语时,简要地把它记录下来。然后在计划的时间中深入研究它。
了解新行情:互联网上有大量关于学习新技术的资源。阅读社区讨论和邮件列表,可以了解其他人遇到的问题,以及他们发现的很酷的解决方案。选择一些公认的优秀技
术博客,经常去读一读,以了解那些顶尖的博客作者们正在关注什么
参加本地的用户组活动,听讲座,参加研讨会议: 各种技术在很多地区都会有用户组,加入微信群或者QQ群。听IT先进公司的公开课程。
如饥似渴地阅读:找一些关于软件开发和非技术主题的好书(我们很乐意为你推荐),也可以是一些专业的期刊和商业杂志,甚至是一些大众媒体新闻(有趣的是在那里常常能看到老技术被吹捧为
新潮流)。
跟踪技术变化: 你不需要精通所有技术,但需要清楚知道行业的动向,从而规划你的项目和职业生涯。

平衡的艺术
1.许多新想法从未变得羽翼丰满,成为有用的技术。即使是大型、热门和资金充裕的项目也会有同样的下场。你要正确把握自己投入的精力。
2.你不可能精通每一项技术,没有必要去做这样的尝试。只要你在某些方面成为专家,就能使用同样的方法,很容易地成为新领域的专家。
3.你要明白为什么需要这项新技术——它试图解决什么样的问题?它可以被用在什么地方?
4.避免在一时冲动的情况下,只是因为想学习而将应用切换到新的技术、框架或开发语言。在做决策之前,你必须评估新技术的优势。开发一个小的原型系统,是对付技术狂热者的一剂良药。

3.让设计指导而不是操纵开发

好的设计应该是正确的,而不是精确的。也就是说,它描述的一切必须是正确的,不应该涉及不确定或者可能会发生变化的细节。好设计是一张地图,它也会进化。设计指引你向正确的方向前进,它不应该标识具体的路线,是目标,不是具体的处方。你不要被设计(或者设计师)操纵。

“设计”是软件开发过程不可缺少的步骤。它帮助你理解系统的细节,理解部件和子系统之间的关系,并且指导你的实现。一些成熟的方法论很强调设计,例如,统一过程(Unified Process,UP)十分重视和产品相关的文档。项目管理者和企业主常常为开发细节困扰,他们希望在开始编码之前,先有完整的设计和文档。毕竟那也是你如何管理桥梁或建筑项目的。
另一方面,敏捷方法建议你早在开发初期就开始编码,但不就意味着没有设计,好的设计仍然十分重要。画关键工作图(例如,用UML)是必不可少的,因为要使用类及其交互关系来描绘系统是如何组织的。在做设计的时候,你需要花时间去思考(讨论)各种不同选择的缺陷和益处,以及如何做权衡。然后下一步才考虑是否需要开始编码。如果你在前期没有考虑清楚这些问题,就草草地开始编码,很可能会被很多意料之外的问题搞晕。
但是,即使之前已经提交了设计文档,也还会有一些意料之外的情况出现。时刻谨记,此阶段提出的设计只是基于你目前对需求的理解而已。一旦开始了编码,一切都会改变。设计及其代码实现会不停地发展和变化。
一些项目领导和经理认为设计应该尽可能地详细,这样就可以简单地交付给“代码工人们”。他们认为代码工人不需要做任何决定,只要简单地把设计转化成代码就可以了,没有一个愿意在这样的团队中做纯粹的打字员。
如果系统和已有代码的现状表明接收到的设计不够理想,那该怎么办?太糟糕了!时间已经花费在设计上,没有工夫回头重新设计了。团队会死撑下去,用代码实现了明明知道是错误的设计。这听起来是不是很愚蠢?是够愚蠢的,但是有一些公司真的就是这样做的。严格的需求—设计—代码—测试开发流程源于理想化的瀑布式开发方法,它导致在前面进行了过度的设计。这样在项目的生命周期中,更新和维护这些详细的设计文档变成了主要工作,需要时间和资源方面的巨大投资,却只有很少的回报。我们本可以做得更好。
设计可以分为两层:战略和战术,前期的设计属于战略,通常只有在没有深入理解需求的时候需要这样的设计。更确切地说,它应该只描述总体战略,不应深入到具体的细节。战略级别的设计不应该具体说明程序方法、参数、字段和对象交互精确顺序的细节。那应该留到战术设计阶段,它应该在项目开发的时候再具体展开。不要一开始就进行战术设计,它的重点是集中在单个的方法或数据类型上。这时,更适合讨论如何设计类的职责。因为这仍然是一个高层次、面向目标的设计。事实上,CRC(类—职责—协作)卡片的设计方法就是用来做这个事情的。每个类术语描述。类名。职责:它应该做什么?协作者:要完成工作它要与其他什么对象一起工作?
如何知道一个设计是好的设计,或者正合适?代码很自然地为设计的好坏提供了好的反馈。如果需求有了小的变化,它仍然容易去实现,那么它就是好的设计。而如果小的需求变化就带来一大批基础代码的破坏,那么设计就需要改进。

平衡的艺术
“不要在前期做大量的设计”并不是说不要设计。只是说在没有经过真正的代码验证之前,不要陷入太多的设计任务。当对设计一无所知的时候,投入编码也是一件危险的事。如果深入编码只是为了学习或创造原型,只要你随后能把这些代码扔掉,那也是一个不错的办法。
即使初始的设计到后面不再管用,你仍需设计:设计行为是无价的。正如美国总统艾森豪威尔所说:“计划是没有价值的,但计划的过程是必不可少的” 在设计过程中学习是有价值的,但设计本身也许没有太大的用处。 白板、草图、便利贴都是非常好的设计工具。复杂的建模工具只会让你分散精力,而不是启发你的工作。

4.提早实现自动化部署

系统的安装或者部署应该简单、可靠及可重复。一切都很自然。

系统能在开发者,测试人员,用户的机器上,生产环境中部署运行当然很好,但是这就意味着,你要能用一种可重复和可靠的方式,在目标机器上部署你的应用。不幸的是,大部分开发者只会在项目的尾期才开始考虑部署问题。结果经常出现部署失败,要么是少了依赖的组件,要么是少了一些图片,要么就是目录结构有误。
如果开发者改变了应用的目录结构,或者是在不同的应用之间创建和共享图片目录,很可能会导致安装过程失败。当这些变化在人们印象中还很深的时候,你可以快速地找到各种问题。但是几周或者几个月之后查找它们,特别是在给客户演示的时候,可就不是一件闹着玩的事情了。
如果还是手工安装应用,那么后把应用部署到生产环境时会发生什么呢?就算公司给你加班费,你也不愿意为不同用户的机器或不同地点的服务器上一遍又一遍地安装应用。 有了自动化部署系统后,在项目开发的整个过程中,会更容易适应互相依赖的变化。很可能你在安装系统的时候,会忘记添加需要的库或组件——在任意一台机器上运行自动化安装程序,你很快就会知道什么丢失了。如果因为缺少了一些组件,从第一天起就开始交付一开始就进行全面部署,而不是等到项目的后期,这会有很多好处。事实上,有些项目在正式开发之前,就设置好了所有的安装环境。要求大家为预期客户实现一个简单的功能演示——验证一个概念的可行性。即使项目还没有正式开始,我们就有了单元测试、持续集成和基于窗口的安装程序。这样,我们就可以更容易更简单地给用户交付这个演示系统:用户所要做的工作,就是从我们的网站上点击一个链接,然后就可以自己在各种不同的机器上安装这个演示系统了。
在签约之前,就能提供出如此强大的演示,这无疑证明了我们非常专业,具有强大的开发能力。一开始就实现自动化部署应用。使用部署系统安装你的应用,在不同的机器上用不同的配置文件测试依赖的问题。质量保证人员要像测试应用一样测试部署。
平衡的艺术
1.一般产品在安装的时候,都需要有相应的软、硬件环境。比如,Java或Ruby 的某个版本、外部数据库或者操作系统,检查依赖关系,也是安装过程的一部分。
2. 在没有询问并征得用户的同意之前,安装程序绝对不能删除用户的数据。提早实现自动化部署
3. 部署一个紧急修复的bug应该很简单,特别是在生产服务器的环境中。但是在凌晨3点半,你还在手工部署系统。
4. 用户应该可以安全并且完整地卸载安装程序,特别是在质量保证人员的机器环境中。
5. 如果维护安装脚本变得很困难,那很可能是一个早期警告,预示着——很高的维护成本(或者不好的设计决策)。

5.度量真实的进度

清楚项目的真实进度,是一项强大的技术。度量剩下的工作量。不要用不恰当的度量来欺骗自己或者团队。要评估那些需要完成的待办事项。使用待办事项及个人与项目管理工具的列表的更多信息,用自己的时间表报告工作进度,用它做项目计划。不用管那些实际的工作时间,每周填满40小时就可以了。

时间的消逝(通常很快)可以证明:判断工作进度 好是看实际花费的时间而不是估计的时间。你说早已经用时间表进行了追踪。不幸的是,几乎所有公司的时间表都是为工资会计准备的,不是用来度量软件项目的开发进度的。例如,如果你工作了60 个小时,也许你的老板会让你在时间表上只填写40个小时,这是公司会计想看到的。所以,时间表很难真实地反映工作完成状况,因此它不可以用来进行项目计划、评估或表现评估。即使没有时间表,一些开发人员还是很难面对现实了,专注于你的方向解自己的真实进度。你曾经听到开发人员报告一个任务完成了80%吗?然而过了一天又一天,一周又一 Focus on where 周,那个任务仍然是完成了80%?随意用一个比率进行度量是没有意义的,这就好比是说80%是对的(除非你是政客,否则对和错应该是布尔条件)。所以,我们不应该去计算工作量完成的百分比,而应该测定还剩下多少工作量没有完成。如果你
初估计这个任务需要40个小时,在开发了35
个小时之后,你认为还需要另外30个小时的工作。那就得到了很重要的度量结果(这里诚实非常重要,隐瞒真相毫无意义)。
在你后真正完成一项任务时,要清楚知道完成这个任务真正花费的时间。奇怪的是,它花费的时间很可能要比 初估计时间长。没有关系,我们希望这能作为下一次的参考。在为下一个任务估计工作量时,可以根据这次经验调整评估。如果你低估了一个任务,评估是2天,它后花费了6天,那么系数就是3。除非是异常情况,否则你应该对下次估计乘以系数3。你的评估会波动一段时间,有时候过低估计,有时候过高估计。但随着时间的推移,你的评估会与事实接近,你也会对任务所花费的时间有更清楚的认识。

度量真实的进度,如果能一直让下一步工作是可见的,会有助于进度度量。 好的做法就是使用待办事项(backlog)。待办事项就是等待完成的任务列表。当一个任务被完成了,它就会从列表中移除(逻辑上的,而物理上就是把它从列表中划掉,或者标识它是完成的状态)。当添加新任务的时候,先排列它们的优先级,然后加入到待办事项中。你也可以有个人的待办事项、当前迭代的待办事项或者整个项目的待办事项。通过代办事项,就可以随时知道下一步 重要的任务是什么。同时,你的评估技巧也在不停地改进,你也会越来越清楚完成一项任务要花费的时间。

Scrum方法中的sprint
在Scrum(迭代增量开发方法),每个迭代被称作sprint,通常为30天时间。sprint 的待办事项列表是当前迭代任务列表,它会评估剩下的工作量,显示每个任务还需要多少小时可以完成。每个工作日,每个团队成员会重新评估完成一个任务还需要多少小时。不管怎么样,只要所有任务的评估总和超过了一个迭代剩余的时间,那么任务就必须移到下一个迭代中开发。如果每月还有一些剩余的时间,你还可以添加新的任务。这样做,客户一定会非常喜欢并且你会觉得很舒服,因为你很清楚哪些任务已经完成,哪些是没有完成的,以及它们的优先级。

平衡的艺术

  1. 6分钟作为一个时间单位,它的粒度实在太细了,这不是敏捷的做法。一周或者一个月的时间单元,它的粒度太粗了,这也不是敏捷的做法。需要关注功能,而不是日程表。
  2. 如果你在一个项目中花费了很多时间来了解你所花费的时间,而没有足够的时间进行工作,那么你在了解你所花费的时间上花费的时间就太多了。
  3. 一周工作40个小时,不是说你就有40个小时的编码时间。你需要减去会议、电话、电子邮件以及其他相关活动的时间。

6.用代码沟通

注释就像是可以帮助你的好朋友,可以先阅读注释,然后快速浏览代码,从而完全理解它做了什么,以及为什么这样做。建立代码文档无外乎两种方式:利用代码本身;利用注释来沟通代码之外的问题。用注释沟通。使用细心选择的、有意义的命名。用注释描述代码意图和约束。注释不能替代优秀的代码。

“如果代码太杂乱以至于无法阅读,就应该使用注释来说明。精确地解释代码做了什么,每行代码都要加注释。不用管为什么要这样编码,只要告诉我们到底是怎么做的就好了。”通常程序员都很讨厌写文档。这是因为大部分文档都与代码没有关系,并且越来越难以保证其符合目前的新状况。这不只违反了DRY原则(不要重复你自己,
Don’t Repeat Yourself),还会产生使人误解的文档,这还不如没有文档。
如果必须通读一个方法的代码才能了解它做了什么,那么开发人员先要投入大量的时间和精力不要用注释来包裹你的代码 。反过来讲,只需短短几行注释说明方法行为,就可以让生活变得轻松许多。开发人员可以很快了解到它的意图、它的期待结果,以及应该注意之处这可省了你不少劲儿。应该文档化你所有的代码吗?在某种程度上说,是的。但这并不意味着要注释绝大部分代码,特别是在方法体内部。源代码可以被读懂,不是因为其中的注释,而应该是由于它本身优雅而清晰——变量名运用正确、空格使用得当、逻辑分离清晰,以及表达式非常简洁。
如何命名很重要。程序元素的命名是代码读者必读的部分。通过使用细心挑选的名称,可以向阅读者传递大量的意图和信息。反过来讲,使用人造的命名范式会让代码难以阅读和理解。这些范式中包括的底层数据类型信息,会硬编码在变量名和方法名中,形成脆弱、僵化的。知道一件事物的真实名称可以让。
使用细心挑选的名称和清晰的执行路径,代码几乎不需要注释。许多注释没有传递任何有意义的信息,这种注释只会分散注意力,而且很容易失去时效性。
注释可用来为读者指定一条正确的代码访问路线图。为代码中的每个类或模块添加一个短小的描述,说明其目的以及是否有任何特别需求。对于类中的每个方法,可能要说明下列信息。
1.目的:为什么需要这个方法?
2.需求(前置条件):方法需要什么样的输入,对象必须处于何种状态,才能让这个方法工作?
3.承诺(后置条件):方法成功执行后,对象现在处于什么状态,有哪些返回值?
4.异常:可能会发生什么样的问题?会抛出什么样的异常?

文档不只是为团队或组织之外的人准备的。假定你要修改几个月之前所写的代码,如果只要看一下方法头上的注释,就知道需要了解的重要细节,那么修改起来是不是会方便很多。

平衡的艺术
1.花时间去写简明扼要的注释吧,代码可以传递意图的地方不要使用注释
2.解释代码做了什么的注释用处不那么大。相反,注释要说明为什么会这样写代码。
3.当重写方法时,保留描述原有方法意图和约束的注释。

7.编写内聚的代码

编写新的代码,首先要决定的就是把这些代码放在什么地方。如果所有的代码都在一个类或组件里面,要找起来是很方便的。内聚性用来评估一个组件(包、模块或配件)中成员的功能相关性。内聚程度高,表明各个成员共同完成了一个功能特性或是一组功能特性。内聚程度低的话,表明各个成员提供的功能是互不相干的。每个类或组件只做一件事,而且做得很好。bug 很容易跟踪,代码也易于修改,因为类和组件的责任都很清晰。

如何组织一个组件中的代码,会对开发人员的生产力和全部代码的可维护性产生重要影响。在决定创建一个类的时候,问问自己,这个类的功能是不是与组件中其他某个类的功能类似,而且功能紧密相关。这就是组件级的内聚性。类也要遵循内聚性。如果一个类的方法和属性共同完成了一个功能(或是一系列紧密相关的功能),这个类就是内聚的。

假定要对数据库的表结构进行一次微调。这个微小的变化会导致应用中所有的页面发生变化,而且每个页面中都会有多处改变——这个应用很快就变成了一场灾难。如果应用使用了中间层对象(比如一个COM组件)来访问数据库,数据库表结构变更所造成的影响就可以控制在一定的范围之内,代码也更容易维护。
低内聚性的代码会造成很严重的后果。假设有这样一个类,实现了五种完全不相干的功能。如果这5个功能的需求或细节发生了变化,这个类也必须跟着改变。如果一个类(或者一个组件)变化得过于频繁,这样的改变会对整个系统形成“涟漪效应”,并导致更多的维护和成本的发生。考虑另一个只实现了一种功能的类,这个类变化的频度就没有那么高。类似地,一个更具内聚性的组件不会有太多导致其变化的原因,也因此而更加稳定。根据单一职责原则一个模块应该只有一个发生变化的原因。
一些设计技巧可以起到帮助作用。举例来说,我们常常使用模型—视图—控制器(MVC)模式来分离展示层逻辑、控制器和模型。这个模式非常有效,因为它可以让开发人员获得更高的内聚性。模型中的类包含一种功能,在控制器中的类包含另外的功能,而视图中的类则只关心UI。内聚性会影响一个组件的可重用性。组件粒度是在设计时要考虑的一个重要因素。根据重用发布等价原则:重用的粒度与发布的粒度相同。这就是说,程序库用户所需要的,是完整的程序库,而不只是其中的一部分。如果不能遵循这个原则,组件用户就会被强迫只能使用所发布组件的一部分。很不幸的是,他们仍然会被不关心的那一部分的更新所影响。软件包越大,可重用性就越差。 让类的功能尽量集中,让组件尽量小。要避免创建很大的类或组件,也不要创建无所不包的大杂烩类。

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

8.根据契约进行替换

保持系统灵活性的关键方式,是当新代码取代原有代码之后,其他已有的代码不会意识到任何差别。深层次的继承是很棒的。如果你需要其他类的函数,直接继承它们就好了!不要担心你创建的新类会造成破坏,你的调用者可以改变他们的代码。这是他们的问题,而不是你的问题。

某个开发人员可能想为通信的底层架构添加一种新的加密方式,或者使用同样的接口实现更好的搜索算法。只要接口保持不变,开发人员就可以随意修改实现代码,而不影响其他任何现有代码。然而,说起来容易,做起来难。所以需要一点指导来帮助我们正确地实现。
Liskov替换原则告诉我们:任何继承后得到的派生类对象,必须可以替换任何被使用的基类对象,而且使用者不必知道任何差异。换句话说,某段代码如果使用了基类中的方法,就必须能够使用派生类的对象,并且自己不必进行任何修改。要遵守Liskov替换原则,相对基类的对应方法,派生类服务(方法)应该不要求更多,不承诺更少;要可以进行自由的替换。在设计类的继承层次时,这是一个非常重要的考虑因素。继承是OO建模和编程中被滥用多的概念之一。如果违反了Liskov替换原则,继承层次可能仍然可以提供代码的可重用性,但是将会失去可扩展性。类继承关系的使用者现在必须要检查给定对象的类型,以确定如何针对其进行处理。当引入了新的类之后,调用代码必须经常重新评估并修正,这不是敏捷的方式。但是可以借用一些帮助。

当使用继承时,要想想派生类是否可以替换基类。如果答案是不能,就要问问自己为什么要使用继承。如果答案是希望在编写新类的时候,还要重用基类中的代码,也许要考虑转而使用聚合。聚合是指在类中包含一个对象,并且该对象是其他类的实例,开发人员将责任委托给所包含的对象来完成。
那么继承和聚合分别在什么时候使用呢?
1.如果新类可以替换已有的类,并且它们之间的关系可以通过is-a来描述,就要使用继承。
2. 如果新类只是使用已有的类,并且两者之间的关系可以描述为has-a或是uses-a,就使用聚合吧。
开发人员可能会争辩说,在使用聚合时,必须要写很多小方法,来将方法调用指向所包含的对象。在继承中,不需要这样做,因为基类中的公共方法在派生类中就已经是可用的了。仅凭这一点,并不能构成使用继承足够好的理由。通过替换代码来扩展系统。通过替换遵循接口契约的类,来添加并改进功能特性。要多使用委托而不是继承。

平衡的艺术

  1. 相对继承来说,聚合更加灵活,适应力也更强。
  2. 继承不是魔鬼,只是长久以来被大家误解了。
  3. 如果你不确定一个接口做出了什么样的承诺,或是有什么样的需求,那就很难提供一个对其有意义的实现了。

9.报告所有的异常

不要让程序的调用者看到那些奇怪的异常。处理它们是你的责任。把你调用的一切都包起来,然后发送自己定义的异常或者干脆自己解决掉。代码刚刚完成时,是寻找问题的佳时机。如果放任不管,它也不会变得更好。

从事任何编程工作,都要考虑事物正常状况下是如何运作的。不过更应该想一想,当出现问题——也就是事情没有按计划进行时,会发生什么。在调用别人的代码时,它也许会抛异常,这时我们可以试着对其处理,并从失败中恢复。当然要是在用户没有意识到的情况下,可以恢复并继续正常处理流程,这就好不过了。要是不能恢复,应该让调用代码的用户知道,到底是哪里出现了问题。不过也不尽然。

平衡的艺术

  1. 决定由谁来负责处理异常是设计工作的一部分。
  2. 不是所有的问题都应该抛出异常。
  3. 报告的异常应该在代码的上下文中有实际意义。比如抛出一个
    NullPointerException起不到任何帮助作用。
  4. 如果代码中会记录运行时调试日志,当捕获或是抛出异常时,都要记录日志信息;这样做对以后的跟踪工作很有帮助。
  5. 检查异常处理起来很麻烦。没人愿意调用抛出31种不同检查异常的方法。这是设计上的问题:要把它解决掉,而不是随便打个补丁就算了。
  6. 要传播不能处理的异常。

10.做代码复查

要寻找深藏不露的程序bug,正式地进行代码检查,其效果是任何已知形式测试的两倍,而且是移除80%缺陷的唯一已知方法。——Capers Jones的《估算软件成本》
代码复查或许是找到并解决问题的佳方式。然而,有时很难说服管理层和开发人员使用它来完成开发工作。管理层担心进行代码复查所耗费的时间。他们不希望团队停止编码,而去参加长时间的代码复查会议。开发人员对代码复查感到担心,允许别人看他们的代码,会让他们有受威胁的感觉。这影响了他们的自尊心。他们担心在情感上受到打击,代码复查不只针对初级开发者编写的代码——团队中每个开发人员的代码都应该进行复查,无论其经验丰富与否。代码复查随着开发活动持续进行,而且每次针对的代码量相对较少。感觉复查活动就像是项目正在进行的一部分,而不是一种令人畏惧的事情。

如何进行代码复查基本方案
1.捡拾游戏。当某些代码编写完成、通过编译、完成测试,“提交复查”是一种快速而非正式的方式,保证代码在提交之前是可以被接受的。为了消除行为上的惯性,要在开发人员之间进行轮换。比如,如果A的代码上次是由B复查的,这次不妨让C来复查。这是一种很有效的技术。
2.结对编程。在极限编程中,不存在一个人独立进行编码的情况。编程总是成对进行的:一个人在键盘旁边(担任司机的角色),另一个人坐在后面担任导航员。他们会不时变换角色。有第二双眼睛在旁边盯着,就像是在进行持续的代码复查活动,也就不必安排单独的特定复查时间了。

在代码复查中要看什么呢?
你可能会制订出要检查的一些特定问题列表(所有的异常处理程序不允许空,所有的数据库调用都要在包的事务中进行等等),不过这里是一个可供启动的基本的检查列表。

  1. 代码能否被读懂和理解?
  2. 是否有任何明显的错误?
  3. 代码是否会对应用的其他部分产生不良影响?
  4. 是否存在重复的代码(在复查的这部分代码中,或是在系统的其他部分代码)?
  5. 是否存在可以改进或重构的部分?

复查所有的代码。对于提升代码质量和降低错误率来说,代码复查是无价之宝。如果以正确的方式进行,复查可以产生非常实用而高效的成果。
要让不同的开发人员在每个任务完成后复查代码。

平衡的艺术

  1. 不进行思考、类似于橡皮图章一样的代码复查没有任何价值。
  2. 代码复查需要积极评估代码的设计和清晰程度,而不只是考量变量名和代码格式是否符合组织的标准。
  3. 同样的功能,不同开发人员的代码实现可能不同。差异并不意味着不好。除非你可以让某段代码明确变得更好,否则不要随意批评别人的代码。
  4. 如果不及时跟进讨论中给出的建议,代码复查是没有实际价值的。可以安排跟进会议,或者使用代码标记系统,来标识需要完成的工作,跟踪已经处理完的部分。
  5. 要确保代码复查参与人员得到每次复查活动的反馈。作为结果,要让每个人知道复查完成后所采取的行动。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章