读书之前首先看到的内容,让大家知道这不是一本烂书,本书从项目管理,软件架构和设计、代码编写和测试,各方面都有精彩的阐述。序中的重点内容:
《领悟程序员的哲学》
《再次阅读,感受颇多》
《一切阅读都是误读》
《程序员升级必备》
《程序员心底的小声音》
读书分享《程序员修炼之道》
0.0几篇序 1
第一章 注重实效的哲学 3
1我的源码让猫给吃了 3
2软件的熵 3
3石头汤与煮青蛙 4
4足够好的软件 4
5你的知识资产 5
6交流 6
第二章 注重实效的途径 6
7重复的危害 6
8正交性 7
9可撤销性 9
10曳光弹 9
11原型与便笺 10
12领域语言 11
13估算 11
第三章 基本工具 11
14纯文本的威力 11
15shell游戏 12
16强力编辑 12
17源码控制 12
18调试 13
19文本操纵 13
20代码生成器 13
第四章 注重实效的偏执 14
21按合约设计 14
22死程序不说谎 14
23断言式编程 14
24何时使用异常 14
25怎样配平资源 15
第五章 弯曲,或折断 17
26解耦与得墨忒耳法则 17
27元程序设计 18
28时间耦合 18
29它只是视图 18
30黑板 18
第六章 当你编码时 19
31靠巧合编程 19
32算法效率 20
33重构 20
34易于测试的代码 23
35邪恶的向导 23
第七章 在项目开始之前 26
36需求之坑 26
37解开不可能解开的谜题 26
38等你准备好 26
39规范陷阱 26
40圆圈与箭头 27
第八章 注重实效的项目 28
41注重实效的团队 28
42无处不在的自动化 28
43无情的测试 28
44全都是写 30
45极大的期望 30
46傲慢与偏见 30
不要说做不到:要说明能够做什么挽回局面。必须扔掉代码?可以讲讲重构的价值。要花时间建立原型,来确定最好的前进路线?还是要引入更好的测试或自动化,防止问题再度发生?又或许是其他的额外资源。责任是你主动担负的东西
Provide Options, Don’t Make Lame Excuses
提供各种选择,而不是各种蹩脚的借口
当软件中的无序增长时,程序员们称之为“软件腐烂”(software rot)。有许多因素可以促生软件腐烂。其中最重要的一个似乎是开发项目时的心理(或文化)。引出“破窗户理论”。
Don’t Live with Broken Windows
不要容忍破窗户
一扇破窗户——一段设计低劣的代码、团队必须在整个项目开发过程中加以忍受的一项糟糕的管理决策——就足以使项目开始衰败。如果你发现自己在有好些破窗户的项目里工作,会很容易产生这样的想法:“这些代码的其余部分也是垃圾,我只要照着做就行了。”项目在这之前是否一直很好,并没有什么关系。
这是一种促成变化的策略,在有些情况下,你也许确切地知道需要做什么,以及怎样去做。整个系统就在你的眼前——你知道它是对的。设计出你可以合理要求的东西,好好开发它。一旦完成,就拿给大家看,人们知道,参与正在发生的成功要更容易,所以再增加其他功能就会容易的多,万事开头难的意思。
Be a Catalyst for Change
做变化的催化剂
另一方面,石头汤的故事也是关于温和而渐进的欺骗的故事。我们都看见过这样的症状。项目慢慢地、不可改变地完全失去控制。大多数软件灾难都是从微不足道的小事情开始的,大多数项目的拖延都是一天一天发生的。系统一个特性一个特性地偏离其规范,一个又一个的补丁被打到某段代码上,直到最初的代码一点没有留下。
Remember the Big Picture
记住大场景
在破窗户理论中,人们失去与熵战斗的意愿,是因为他们觉察到没有人会在意。而青蛙只是没有注意到变化。
不要像青蛙一样。留心大图景。要持续不断地观察周围发生的事情,而不只是你自己在做的事情。
Make Quality a Requirements Issue
使质量成为需求问题
今天的了不起的软件常常比明天的完美软件更可取。如果你给用户某样东西,让他们及早使用,他们的反馈常常会把你引向更好的最终解决方案。不要因为过度修饰和过于求精而毁损完好的程序。继续前进,让你的代码凭着自己的质量站立一会儿。它也许不完美,但不用担心:它不可能完美
在一切还未掌握之前,需要拥有广泛的知识和经验基础才能赢得这一切。学习是一个持续不断的过程,在本小节中讨论一些策略,让我们“开足马力”。
随着新技术、语言及环境的出现,你的知识会变得过时。不断变化的市场驱动力也许会使你的经验变得陈旧或无关紧要。
随着你的知识的价值降低,对你的公司或客户来说,你的价值也在降低。我们想要阻止这样的事情,决不让它发生。
Invest Regularly in Your Knowledge Portfolio
定期为你的知识资产投资
1. 每年至少学习一种新语言
2. 每季度阅读一本技术书籍
3. 也要阅读非技术书籍
4. 上课
5. 参加本地用户组织
6. 试验不同的环境
7. 跟上潮流
8. 上网
持续投入十分重要,一旦你熟悉了某种新语言或新技术,继续前进,学习另外一种。设法把你学到的东西应用到你当前的项目中。即使你的项目没有使用该技术,你或许也能借鉴一些想法。
如果你自己找不到答案,就去找能找到答案的人。不要把问题搁在那里,与他人交谈可以帮助你建立人际网络,而因为在这个过程中找到了其他不相关问题的解决方案,你也许还会让自己大吃一惊。
所有阅读和研究都需要时间,而时间已经很短缺。所以你需要预先规划,让自己在空闲的片刻时间里总有东西可读。
批判的思考你读到的和听到的。你需要确保你的资产中的知识是准确的,并且没有受到供应商或媒体炒作的影响。
我们不是活在真空世界,需要花大量时间与人交流。只有当你是在传达信息时,你才是在交流。
有效交流的几种方法:知道你想要说什么,了解你的听众,选择时机,选择风格,让文档美观,让听众参与,做倾听者,回复他人。
Critically Analyze What You Read and Hear
批判地分析你读到的和听到的
It’s Both What You Say and the Way You Say It
你说什么和你怎么说同样重要
“重复的危害”提醒你,不要在系统各处对知识进行重复。作为程序员,我们收集、组织、维护和利用知识。我们在规范中记载知识、在运行的代码中使其活跃起来并将其用于提供测试过程中所需的检查。遗憾的是,知识并不稳定。所有这些不稳定都意味着我们要把很大一部分时间花在维护上,重新组织和表达我们的系统中的知识。程序员须持续不断地维护。我们的理解逐日变化,当我们设计或编码时,出现了新的需求。环境或许变了。不管原因是什么,维护都不是时有时无的活动,而是整个开发过程中的例行事务。可靠地开发软件、并让我们的开发更易于理解和维护的惟一途径,是遵循我们称之为DRY的原则:
系统中的每一项知识都必须具有单一、无歧义、权威的表示。
DRY – Don’t Repeat Yourself
不要重复你自己
DRY原则是注重实效的程序员的工具箱里最重要的工具之一。我们所见到的大多数重复都可归入下列范畴:
1. 强加的重复(imposed duplication)。开发者觉得他们无可选择——不同的环境似乎要求重复。
2. 无意的重复(inadvertent duplication)。开发者没有意识到他们在重复信息。
3. 无耐性的重复(impatient duplication)。开发者偷懒,他们重复,因为那样似乎更容易。
4. 开发者之间的重复(interdeveloper duplication)。同一团队(或不同团队)的几个人重复了同样的信息。
Make It Easy to Reuse
让复用变得容易
你所要做的是营造一种环境,在其中要找到并复用已有的东西,比自己编写更容易。如果不容易,大家就不会去复用。而如果不进行复用,你们就会有重复知识的风险。
“正交性”提醒你,不要把任何一项知识分散在多个系统组件中。在计算技术中,该术语用于表示某种不相依赖性或是解耦性。如果两个或更多事物中的一个发生变化,不会影响其他事物,这些事物就是正交的。非正交的例子:直升机驾驶操作的各个控制器之间就是相互影响的,不是正交的。
正交的好处就是为了可以局部修正(local fix)。
Eliminate Effects Between Unrelated Things
消除无关事物之间的影响
如果你编写正交的系统,你得到两个主要好处:提高生产率与降低风险
提高生产率
1. 改动得以局部化,所以开发时间和测试时间得以降低。
2. 正交的途径还能够促进复用。
3. 如果你对正交的组件进行组合,生产率会有相当微妙的提高。
降低风险
1. 有问题的代码区域被隔离开来。
2. 所得系统更健壮。
3. 正交系统很可能能得到更好的测试,因为设计测试、并针对其组件运行测试更容易。
4. 你不会与特定的供应商、产品、或是平台紧绑在一起
工作中应用正交原则的几种方式:
项目团队:有些项目团队很有效率,每个人都知道要做什么,而另一些团队的成员却老是在争吵,而且好像无法避免互相妨碍
设计:如果我显著地改变某个特定功能背后的需求,只有1个模块会受影响;你的设计在多大程度上解除了与现实世界中的的变化的耦合?
工具箱与库
在你引入第三方工具箱和库时,要注意保持系统的正交性。要明智地选择技术。
编码
编写代码,都有降低应用正交性的风险。除非你不仅时刻监视你正在做的事情,也时刻监视应用的更大语境,否则,你就有可能无意中重复其他模块的功能,或是两次表示已有的知识。
怎样维持正交性?
1. 让你的代码保持解耦。编写“羞怯”的代码——也就是不会没有必要地向其他模块暴露任何事情、也不依赖其他模块的实现的模块。
2. 避免使用全局数据。
3. 避免编写相似的函数。你常常会遇到看起来全都很像的一组函数——它们也许在开始和结束处共享公共的代码,中间的算法却各有不同。Ps:这个深有体会
4. 养成不断地批判对待自己的代码的习惯。寻找任何重新进行组织、以改善其结构和正交性的机会。这个过程叫做重构(refactoring)。
测试
正交地设计和实现的系统也更易于测试,因为系统的各组件间的交互是形式化的和有限的,更多的系统测试可以在单个的模块级进行。与集成测试(integration testing)相比,模块级(或单元)测试要更容易规定和进行得多。
认同正交性
如果你参加了一个项目,大家都在不顾一切地做出改动,而每一处改动似乎都会造成别的东西出错,回想一下直升机的噩梦。项目很可能没有进行正交的设计和编码。是重构的时候了。但如果你紧密结合DRY原则、运用正交性原则,你将会发现你开发的系统会变得更为灵活、更易于理解、并且更易于调试、测试和维护。
挑战
问题:考虑常在Windows系统上见到的面向GUI的大型工具和在shell提示下使用的短小、但却可以组合的命令行实用工具。哪一种更为正交,为什么?如果正好按其设计用途加以应用,哪一种更易于使用?哪一种更易于与其他工具组合、以满足新的要求?
思路:win上的GUI大型工具是多个功能综合在一起的一个系统,各个功能可能互相依赖,不满足正交,而shell上的命令行工具,功能单一,不需要依赖其他外界条件就可以单独执行,且不影响其他功能的正常使用,满足正交性。Win上的GUI更加易于使用,因为不需要人为的综合各个功能的处理结果进行处理,只需要让系统按照自己的工作流程得到最终结果即可。Shell命令行更容易与其他工具组合,满足新要求,正交的组合更为容易。
There Are No Final Decisions
不存在最终决策
1. 不确定市场部门想怎样部署系统?预先考虑这个问题,你可以支持单机、客户-服务器、或n层模型——只需要改变配置文件。
2. 你可以把第三方产品隐藏在定义良好的抽象接口后面。
3. 无论你使用的是何种机制,让它可撤消。如果某样东西是自动添加的,它也可以被自动去掉。
它适用于新的项目,特别是当你构建从未构建过的东西时。因为你的用户从未见过这样的系统,他们的需求可能会含糊不清。因为你在使用不熟悉的算法、技术、语言或库,你面对着大量未知的事物。同时,因为完成项目需要时间,在很大程度上你能够确知,你的工作环境将在你完成之前发生变化。
Use Tracer Bullets to Find the Target
用曳光弹找到目标
曳光代码并非用过就扔的代码:你编写它,是为了保留它。它含有任何一段产品代码都拥有的完整的错误检查、结构、文档、以及自查。它只不过功能不全而已。
曳光代码方法有许多优点:
1. 用户能够及早看到能工作的东西。
2. 开发者构建了一个他们能在其中工作的结构。
3. 你有了一个集成平台。
4. 你有了可用于演示的东西。
5. 你将更能够感觉到工作进展。
曳光代码 vs. 原型制作
使用原型,你是要探究最终系统的某些具体的方面。使用真正的原型,在对概念进行了试验之后,你会把你捆扎在一起的无论什么东西扔掉,并根据你学到的经验教训重新适当地进行编码。而曳光代码不是写完就扔的代码。
原型不一定要以代码为基础。要为像工作流和应用逻辑这样的动态事物制作原型,便笺(post-it note)就非常好。用户界面的原型则可以是白板上的图形、或是用绘图程序或界面构建器绘制的无功能的模型。(PS:上次实习,开发之前在A4纸上的画图工作)。
但如果你发现自己处在不能放弃细节的环境中,就需要问自己,是否真的在构建原型。或许曳光弹开发方式更适合这种情况。
可以构建原型的事物:
1. 架构。
2. 已有系统中的新功能。
3. 外部数据的结构或内容。
4. 第三方工具或组件。
5. 性能问题。
6. 用户界面设计。
Prototype to Learn
为了学习而制作原型
制作架构原型
7. 主要组件的责任是否得到了良好定义?是否适当?
8. 主要组件间的协作是否得到了良好定义?
9. 耦合是否得以最小化?
10. 你能否确定重复的潜在来源?
11. 接口定义和各项约束是否可接受?
12. 每个模块在执行过程中是否能访问到其所需的数据?是否能在需要时进行访问?
在你着手制作任何基于代码的原型之前,先确定每个人都理解你正在编写用过就扔的代码。对于不知道那只是原型的人,原型可能会具有欺骗性的吸引力。你必须非常清楚地说明,这些代码是用过就扔的,它们不完整,也不可能完整。
计算机语言会影响你思考问题的方式,以及你看待交流的方式。面对不同的环境和问题时,可以开发一套专注于这个领域的编程技术,属于自己的小众语言。
Program Close to the Problem domain
靠近问题领域编程
Estimate to Avoid Surprises
估算,以避免发生意外
在被要求进行估算时,放慢估算速度,并花一点时间仔细检查各步骤,你几乎总能得到更好的结果。时间估算,表示单位很重要,给出125天和25周,六个月的估算,传达的精确度不一样。
Iterate the Schedule with the Code
通过代码对进度表进行迭代
主旨:工欲善其事,必先利其器。
持久地存储知识的最佳格式是纯文本。
使用纯文本的缺点:
纯文本优点:
纯文本与非纯文本不熟悉或者不明确概念的可以看转载的这个文章:http://blog.csdn.net/recsysml/article/details/44195793
利用命令shell的力量,提高效率。
推荐使用Emacs、vi等工具,好的编辑器特性:
做了一个对比:notepad和vi的生产率,假如要将import的前几行语句按照字母顺序排列,notepad表示只能一个一个调,vi直接命令输入:.,+3!sort,搞定了
总是使用源码控制系统,追踪每一处变动,可撤销性,对于bug追踪、审计、性能及质量等目的,这种信息很宝贵。
要修正问题,而不是发出指责。也不要恐慌。
调试策略:
修正bug后,想一想:以前为什么没有发现这个bug?是不是可以改进单元测试来发现这个bug?如果查找这个bug花了很长时间,问问自己为什么,下一次是不是可以更快地修复类似的bug?
基本工具不能完成的时候,需要文本操纵工具。Unix开发者喜欢shell的力量,并用像awk和sed这样的工具加以增强。偏爱结构化的工具的人喜欢Python面向对象的本质。使用它们可以快速构建实用程序,为你的想法构建原型——传统语言完成这些需要5倍甚至10倍的时间。
类似于工匠面临一再重复制作同一样东西的任务时,它们会制造模板或建造工具。程序员也不例外。
编写能编写代码的代码。
简称DBC:
早崩溃。发现问题,就要让它在问题的现场崩溃,不要跑到调用的栈顶再告诉你发生了什么。
如果它不可能发生,就用assert。极端情况下1个月会少于28天,a=2;b=3;a+b!=5,三角形内角和不是180度,一分钟没有60秒,(a+1)<=a
将异常用于异常的问题。例如:文件读写,例程返回值,各种状态异常检测,服务器超时等异常情况,都需要使用异常,通常为异常分等级,可以抛出异常,或者将异常写入日志。
分配资源的例程要负责释放它。以与资源分配的次序相反的次序解除资源的分配。因为先后2个资源可能会有依赖关系。相同的顺序分配同一组资源。降低死锁的机率。
要会用try{ } finally{ }
要学会Dispose()
总结
对“怎样配平资源”小节特别有感触,在读写文件的问题上,经常造成读写耦合,当时的解决思路和书上的差不多。将读写的打开和关闭文件操作总是维持在同一个地方。打开和关闭时相互对应的。
目标:使耦合减至最少
“得墨忒耳法则”也叫“迪米特法则”
我们从下面这几条基本规则开始:
得墨忒耳定律--对象 O 的 M 方法,可以访问/调用如下的:
1. 对象 O 本身
2. M 方法的传入参数
3. M 方法中创建或实例化的任意对象
4. 对象 O 直接的组件对象
5. 在M范围内,可被O访问的全局变量
一些比喻
我的理解是:常见的踢皮球现象和委托代理
最常见的比喻是:不要和陌生人说话(这是大的规则)
看看这个:假设我在便利店购物。付款时,我是应该将钱包交给收银员,让她打开并取出钱?还是我直接将钱递给她?
PS:再进一步->付款时,我将钱给收银员,但是自己也不清楚钱怎么拿出来的(隐藏属性),自己只负责要钱,要多少钱,实际操作是助理完成并传递给我的,然后在传递给收银员。再做一个比喻:人可以命令一条狗行走(walk),但是不应该直接指挥狗的腿行走,应该由狗去指挥控制它的腿如何行走。
遵循这个规则有什么好处?
高度可配置,不需要重新编译,用纯文本来表示配置元数据可能是一种好的选择。
改变了配置,最好别让用户重启系统。
黑板:数据到达的次序无关紧要;在收到某项事实时,它可以出发适当的规则,反馈也很容易处理;任何规则集的输出都可以张贴到黑板上,并出发更为适用的规则。
黑板模式是一种常用的架构模式,应用中的多种不同数据处理逻辑相互影响和协同来完成数据分析处理。就好像多位不同的专家在同一黑板上交流思想,每个专家都可以获得别的专家写在黑板上的信息,同时也可以用自己的分析去更新黑板上的信息,从而影响其它专家。黑板模式的应用场景是要解决的任务可以分为多个子任务。
传统智慧认为,项目一旦进入编码阶段,工作主要就是机械地把设计转换为可以执行的语句。我们认为,这种态度是导致程序丑陋、低效、结构糟糕、不可维护和完全错误的最大的一个原因。
编码需要对每一次决策进行仔细的思考和判断。不主动思考代码的开发者是靠巧合编程。提倡更积极地参与编码过程。
我们偶尔会开发出一些算法,会让处理器陷入困境。算法效率中,讨论评估代码的速度的方法。
注重实效的程序员批判的思考所有代码。不断思考是否可以有改进的余地,是否可以重构。
易于测试的代码能增加通过测试的可能性。
在邪恶的向导中,建议小心那些替你编写大量代码的工具,除非能理解它们在做什么。
实现的偶然
是指在有限的“测试”下,编写的代码好像能工作,但是一开始就不知道代码为什么能工作。 这正是一种巧合,可能一段时间之后,代码就不能正常工作了。
语境的偶然
不要依靠什么没有保证的东西?
隐含的假定
假定很少被写入文档,不要假定,要测试证明。
怎样深思熟虑地编程
总结:
软件开发者,每天就像工作在雷区,有成百的陷阱等着抓住我们。多余的或不必要的代码可能这次能够正常运行,但换个环境可能就会崩溃,另外会使代码变慢,或引入新的bug。总之,不要靠巧合编程。
尽可能在开发周期的早期抓住并修正错误,道理很简单,但在项目进度压力大的时候,把这句话忘在脑后。为编码工作划定优先级,把时间花在重要的上面,经常也是最难的部分。但如果基础设施不正确,再花哨的界面或装饰也没有什么用。
就是大O表示法,要注意的是,在进行所有的估算之后,唯一作数的计时是代码运行在实际环境中、处理真实数据的速率。所以要测试你的估算。
定义:重写、重做、重新架构代码
何时进行重构
要注意的的是:
本节提倡的是:早重构,常重构。实际中呢?时间压力最大。
不要使用你不理解的向导代码。向导代码确实给你生成了一大堆代码,而且功能也很强大。但它们为你创建了代码,然后就走了。如果你不真正理解它们,将来就会给你带来麻烦。
实例:如果我们要执行一个命令,命令返回的信息,要保存下来继续查找或者判断,有两种比较实用的方法:
方法1:标准IO输入输出,执行cmd用N多种方法,但是比较喜欢subprocess模块,因为功能比较全,for example
见点击打开链接下面部分
实例2:给/etc/rc.local文件打注册码,一开始以为就是打开,然后写入,最后关闭,一个函数搞定就是分分钟的事。
见点击打开链接
项目开始之前,要确定各种需求。
1. 不要搜集需求,挖掘它们。
2. 一种能深入了解用户需求,却未得到足够利用的技术:成为用户。与用户一同工作,像用户一样思考。
3. 维护词汇表,把需求制作成web文档,更多的满足不同听众的需求。
解开谜题的秘诀是真正的约束,有些约束是绝对的,有些是先入之见。
遇到不可能解决的问题时,退一步问问自己如下问题:
1)有更容易的方法吗?
2)你是在设法解决真正的问题,还是被外围的技术问题转移了注意力?
3)这件事情为什么是一个问题?
4)是什么使它如此难以解决?
5)它必须以这种方式完成吗?
6)它真的必须完成吗?
不要在盒子外面思考——要找到盒子。
项目开始之前,到底是良好的判断还是拖延?解决之道,马上开始构建原型,然后进行某种“概念验证”,随着原型的进展,会在某个时刻得到启示。
对有些事情,“做”胜于“描述”,应该倾向于把需求搜集、设计、以及实现视为交付高质量系统的不同方面,而不是简单的作为各个孤立的步骤。
在没有任何支持实现,或没有构建原型,在规范之上构建另外一层规范要小心,因为规定无法构建的东西太容易了。在某个时刻,要进行编码,考虑构建原型,或是考虑曳光弹开发。
不要做形式化方法的奴隶。形式化开发知识一种工具,如果觉得有必要使用,就采用它,但要记住,自己才是主人。批判的看待方法学。
针对团队,重述前面的章节
无论是构建和发布流程、代码复查、其他反复出现的任务,必须是自动的。人工流程不能保证一致性,自动化来确保项目的一致性和可重复性。
早测试,常测试,自动测试
要到通过全部测试,编码才算完成
测试什么
怎样测试
何时进行测试
把代码和文档结合在一起,所有文档都是代码的反映。代码中的注释要规范,像javadoc和DOC++这样的工具,根据源码生成API级的文档。
给用户的东西要比他们期望的多一点。
在你的作品上签名,作为质量的保证。