《程序员修炼之道》读书笔记

第零章 前言

如果要成为一个好程序员,其实所需要的道理也多不了多少,只不过,当水平不够的时候,永远不能认识到那些朴素道理的重要性。

 

最好的入门书永远是《代码大全》,但本书是一本不逊于《代码大全》的伟大著作.

 

本书将帮助你成为更好的程序员。

 

注意实效的程序员有哪些特性:早期的采纳者/快速的改编者,好奇,批判的思考着,有现实感,多才多艺。

 

我们,采集的只是石头,却必须时刻展望未来的大教堂。

 

第一章 注重实效的哲学

开发项目时的心理很重要。

Don't Live with Broken Windows (不要容忍破窗户)。

Be a Catalyst for Change (做变化的催化剂)。

许多用户宁愿今天用上有一些“毛边”的软件,也不愿等待一年后的多媒体版本。

让用户及早的使用,他们的反馈常常会把你引向更好的最终解决方案。

知道何时止步,软件不可能完美。

知识上的投资总能得到最好的回报——本杰明.富兰克林

Invert Regularly in Your KnowLedge Portfolio (定期为你的知识资产投资)

目标:每年至少学习一种新语言。

Critically Analyze What You Read and Hear (批判地分析你读到的和听到的)

不要低估商业主义的力量。即使书店显眼位置摆着一本书,也并不意味着那是一本好书,他们可能是付了钱才放在那里。

It's Both What You Say and the Way You Say it ( 你说什么和怎样说都很重要

交流越有效,你就越有影响力。

第二章 注重实效的途径

当受到诱惑的时候,想一想“欲速则不达”。

DRY——Don't repeat yourself。

一定要阅读他人的源码与文档,访问是互惠的。

非正交系统,例:直升飞机的升降杆控制总升力,但是仅仅控制升降杆会让直升机鼻子向下,需要后翼的配合。

问问你自己:如果我显著的改变某个特定功能背后的需求,有多少模块会受影响?

不要依赖你无法控制的事物属性,如用户的输入,电话号码等等。

了解rmi,corba,ejb,aop思想。

关键的决策不容易撤销。决策会一直变,所以不存在最后总决策。

CORBA这样的技术可以帮助把项目的某些部分与开发语言或平台的变化隔离开来,采用这个架构,你只需要改动替换的组件,而其他组件不受影响。

语言的界限就是一个人的世界界限。

估算多准确才算准确,你父母问你何时到家,或许只是想知道该为你准备午餐还是晚餐。

所有的估算都以问题模型为基础。估算一定需要想一想。常用回答:等等告诉你。

通过代码对进度表进行迭代。

开始和结束处共享代码,中间算法却各不相同,如何才有好的实现,参见《设计模式》的Stratrgy(策略)模式。

第三章 基本工具

工具放大你的才能(IDE、Python等)。

让需求驱动你的采购。

用shell组合出你想要的答案。例:上周哪些java文件没改动。find . -name '*.java' -mtime +7 -print。windows下可用cygwin。

烦恼很多都是自己创造的。

没有人能写出完美的软件,所以调试要占用你大量的时间。

Fix the Problem,Not the Blame(要修正问题,而不是指责)

Don't Assume it —Prove it( 不要假定,要证明

第四章 注重实效的偏执

You Can't Write Perfect Software(你不可能写出完美的软件)

当每个人都确实要对你不利时,偏执就是一个好主意。

用文档记载的内容对程序进行验证。

文档出错时要偏向消费者。

我们很容易掉进“它不可能发生”的一种心理状态。

让程序早点崩溃。

在自责中有一种满足感。当我们责备自己时,会觉得再没人有权责备我们。

用断言确保它不会发生 。assert(stirng!=null).

把断言当做一种调试设备 ,在测试过程中:老鼠可能不会咬断通讯电缆,玩游戏是不会耗尽内存,日志文件不会塞满硬盘。交付使用时关闭断言。

C语言中拥有longjmp/setjmp来代替异常处理。

使用资源有始有终。举例:
void readCustomer(const char *fName,Customer *cRec){
    cFile=fopen(fname,"r+");
    fread(cRec,sizeof(*cRec),1,cFile);
}
void writeCustomer(Customer *cRec){
    rewind(cFile);
    fwrite(cRec,sizeof(*cRec),1,cFile);
    fclose(cFile);
}
void updateCustomer(const char *fName,double newBalance){
    Customer cRec;
    readCustomer(fName,&cRec);
    cRec.balance=newBanlance; 
    writeCustomer(&cRec);
}
1.这是一个糟糕的程序,共享了全局变量cFile,在另一个方法内关闭。文件没关闭的调用:
void updateCustomer(const char *fName,double newBalance){
    Customer cRec;
    readCustomer(fName,&cRec);
    if(newBalance >=0){
        cRec.balance=newBanlance;
        writeCustomer(&cRec);
    }
}
2.一个糟糕的解决方案:
void updateCustomer(const char *fName,double newBalance){
    Customer cRec;
    readCustomer(fName,&cRec);
    if(newBalance >=0){
        cRec.balance=newBanlance;
        writeCustomer(&cRec);
    }else{
        fclose(cFile);
    }
}
3.重构后:
void readCustomer(const char *fName,Customer *cRec){
    fread(cRec,sizeof(*cRec),1,cFile);
}
void writeCustomer(Customer *cRec){
    rewind(cFile);
    fwrite(cRec,sizeof(*cRec),1,cFile);
}
void updateCustomer(const char *fName,double newBalance){
    FILE *cFile;
    cFile=fopen(fname,"r+");
    Customer cRec;
    readCustomer(fName,&cRec);
    cRec.balance=newBanlance;
    if(newBalance >=0){
        cRec.balance=newBanlance;
        writeCustomer(&cRec);
    }
    fclose(cFile);
}

4.现在updateCustomer承担了关于文件的所有责任,它打开并在结束前关闭,还移除了丑陋的全局变量。

 

第五章 弯曲,或折断

好篱笆促成好邻居。


得墨忒耳定律
    1.每个单元对于其他的单元只能拥有有限的知识;
    2.每个单元只能和它的朋友交谈:不能和陌生单元交谈;
    3.只和自己直接的朋友交谈。


用元数据描述应用的配置选项:调节参数,用户偏好,安装目录,等等。(*.ini,property)


抽象放入代码,细节放入元数据。


分析工作流程,以改善并发性。


模型1.发布、订阅模型 。订阅者需要注册,发布者有新消息会通知注册的人,可用在GUI中。


模型2.mvc模型


对象和代码模块写在黑板 上,让每个人都看。可以知道在哪里匿名,交换数据,等等。让所有人都参与行动起来。

黑板系统最初是为了人工孩子能的应用发明的,在那些应用中,要解决——语音识别,只是推理,等等。
当信息很多是,组织你的黑板。
Use Blackboards to Coordinate Workflow(用黑板协调工作流)

 

第六章 当你编码时

不主动思考代码的开发者是在靠巧合编程。不能靠运气和偶然的成功,要深思熟虑。


不知道代码为什么失败,是因为一开始就不知道代码为什么能工作

 

编码是考虑几个理由:
    它也许不是真的能工作。
    他依靠的边界条件也许只是一个偶然。
    没有记入文档的行为可能会随着库的下一次发布而变化。
    多余的和不必要的调用会使你的代码变慢。
    多余的调用有带来新的bug风险。

 

怎样深思熟虑的编程:
    总要意识到你在做什么。
    不要盲目的编程。
    按照计划行事。
    依靠可靠的事物。
    为你的假设建立文档。
    不要只测试代码,还要测试假设。
    为你的工作划分优先级。
    不要做历史的奴隶。不要让已有的代码支配将来的代码


线性的算法几乎不需要关心性能,但是大多数算法都是亚线性的。例如二分查找O( ln(n) ),快速排序O( nln(n) )。
如果你有一个O(n2)的算法,设法将为O ( nln(n) )的分而治之的途径 。碰到阶乘的情况可以选择启发式算法来减少算法运行时间。


批判地思考所有代码,包括我们自己的


最好的并非总是最好的


代码重复、过时、性能、非正交设计时就可以考虑重构了。


Refactor Early,Refactor Often(早重构,常重构)。


不要试图在重构的同时增加功能。开始重构之前要拥有良好的测试。采用短小的步骤开始重构。


编写易于测试的代码


Design to Test(为测试而设计)

 

第七章 在项目开始之前

完美,不是在没有什么需要增加、而是在没有什么需要去掉时达到的。


Don't gather Requirements- Dig for Them(不要搜集需求——挖掘它们)


Work with a User to Think Like a User(与用户一起工作,像用户一样思考 )


建立需求文档。需求不是架构,不是设计,不是用户界面,需求是需要,所以需求文档也不要规定过度。


看远一些,Abstractions Live Longer than Details(抽象比细节活的更长久 )。


把话说出来,在web项目里,需求写在纸上可能就代表已经过时了。


Don't Think Outside the Box-Find the Box(不要在盒子外思考)


一定有更容易的方法。必须以这种方式完成吗?


Listen to Nagging Doubts-Start When You're Ready(倾听反复出现的疑惑——等你准备好了再开始)


Some Things Are Vetter Done than Described(对有些事情,动手做 胜与 描述)


你应该倾向于把需求搜集、设计、实现视为同一过程——交付高质量的代码。


规范有时是个陷阱。

 

第八章 注重实效的项目

Organize Around Functionality,Not Job Functions(围绕功能,而不是工作职责进行组织)


利用makefile,shell,模板,让团队自动化起来。


Test Early,Test Often,Test Automatically(早测试,常测试,自动测试)


通过蓄意破坏来测试,看能否找到加入的bug。


一个bug只抓一次,此后这个bug就应该交给自动测试。


极限测试:内存、磁盘、带宽、磁盘、网络。


好记性不如烂笔头,注释。


Treat English as Just Another Programming Language(把英语当做又一种编程语言 )


Gently Exceed Your Users' Expectations(温和的超过用户的期望 )


Sign Your Work(在你的作品上签名 )

 

读后感

这一本书有10几年的历史了,这些理念似乎现在已经融入到了软件开发里面了,或多或少都接触了。


书有200来页,看着很轻松,都是在表述观点,经历过的表示赞同,没经历过的先留个印象。


书中观点几乎都认同。

 

非常值得看和收藏的书。


作者将表达的每一个观点都浓缩成一句话,写在书的最后,偶尔看一下,能想起不少东西。


书要还了,附上书最后的“注重实效的程序员之快速参考指南”。

 

注重实效的程序员之快速参考指南

1. 关心你的技艺
Care About Your Craft
如果你不在乎能否漂亮地开发出软件,你又为何要耗费生命去开发软件呢?

2. 思考!你的工作
Think! About Your Work
关掉自动驾驶仪,接管操作。不断地批评和评估你的工作。

3. 提供各种选择,不要找蹩脚的借口
Provide Options, Don"t Make Lame Excuses
要提供各种选择,而不是借口。不要说事情做不到;说明能够做什么。

4.不要容忍破窗户
Don"t Live with Broken Windows
当你看到糟糕的设计、错误的决策和糟糕的代码时,修正他们。

5.做变化的催化剂
Be a Catalyst for Change
你不能强迫人们改变。相反,要向他们展示未来可能会怎样,并帮助他们参与对未来的创造。

6. 记住大图景
Remember the Big Picture
不要太过专注于细节,以致忘了查看你周围正在发生什么。

7. 使质量成为需求问题
Make Quality a Requirements Issue
让你的用户参与确定项目真正的需求。

8. 定期为你的知识资产投资
Invest Regularly in Your Konwledge Portfolio
让学习成为习惯

9. 批判的分析你读到的和听到的
Critically Analyze What You Read and Hear
不要被供应商、媒体炒作、或教条左右。要依照你自己的看法和你的项目的情况去对信息进行分析。

10.你说什么和你怎么说同样重要
It"s Both What You Say and the Way You Say It
如果你不能有效地向他人传递你的了不起的想法,这些想法就毫无用处。

11. 不要重复你自己
DRY -- Don"t Repeat Yourself
系统中的每一项知识都必须具有单一、无歧异、权威的表示。

12. 让复用变得容易
Make It Easy to Reuse
如果复用很容易,人们就会去复用。创造一个支持复用的环境。

13. 消除无关事务之间的影响
Eliminate Effects Between Unrelated Things
设计自足、独立、并具有单一、良好定义的目的的组件。

14.不存在最终决策
There Are No Final Decisions
没有决策是浇铸在石头上的。相反,要把每项决策都视为写在沙滩上的,并为变化作好计划。

15.用曳光弹找到目标
Use Tracer Bullets to Find the Target
曳光弹能通过试验各种事物并检查他们离目标有多远来让你追踪目标。

16. 为了学习而制作原型
Prototype to Learn
原型制作是一种学习经验。其价值并不在于所产生的代码,而在于所学到的经验教训。

17.靠近问题领域编程
Program Close to the Problem domain
用你的用户的语言进行设计和编码。

18.估算,以避免发生意外
Estimate to Avoid Surprises
在着手之前先进行估算。你将提前发现潜在的问题。

19.通过代码对进度表进行迭代
Iterate the Schedule with the Code
用你在进行实现时获得的经验提炼项目的时间标度。

20.以纯文本保存知识
Keep Knowledge in Plain Text
纯文本不会过时。它能够帮助你有效利用你的工作,并简化调试和测试。

21.利用命令shell的力量
Use the Power of Command Shells
当图形用户界面无能为力时使用shell

22.用好一种编辑器
Use a Single Editor Well
编辑器应该是你的手的延伸;确保你的编辑器是可配置、可扩展和可编程的。

23.总是使用源码控制
Always Use Source Code Control
源码控制是你的工作时间的机器----你能够回到过去。

24. 要修正问题,而不是发出指责
Fix the Problem, Not the Blame
Bug是你的过错还是别人的过错,并不是真的很有关系----他仍然是你的问题,他仍然需要修正。

25.调试时不要恐慌
Don"t Panic When Debuging
做一次深呼吸,思考什么可能是bug的原因。

26."Select"没有问题
"Select" Isn"t Broken
在 OS或编译器、甚至或是第三方产品或库中很少发现bug。Bug很可能在应用中。

27.不要假定,要证明
Don"t Assume It-Prove It
在实际环境中----使用真正的数据和边界条件----证明你的假定。

28.学习一种文本操纵语言
Learn a Text Manipulation Language
你们每天的很大一部分时间处理文本,为什么不让计算机替你完成部分工作呢?

29. 编写能写代码的代码
Write Code That Writes Code
代码生成器能提高你的生产效率,并有助于避免重复。

30. 你不可能写出完美的软件
You Can"t Write Perfect Software
软件不可能完美。保护你的代码和用户,使它(他)们免于能够预见的错误。

31.通过合约进行设计
Design with Contracts
使用合约建立文档,并检查代码所做的事情正好是他声明要做的。

32.早崩溃
Crash Early
死程序造成的危害通常比有问题的程序要小的多。

33.用断言避免不可能发生的事情
Use Assertions to Prevent the Impossisble
断言验证你的各种假定。在一个不确定的世界里,用断言保护你的代码。

34.将异常用于异常问题
Use Exceptions for Exceptional Problems
异常可能会遭受经典的意大利面条式代码的所有可读性和可维护性问题的折磨。将异常保留给异常的事物。

35.要有始有终
Finish What You Start
只要可能,分配某资源的例程或对象也应该负责解除其分配。

36.将模块之间的耦合减至最少
Minimize Coupling Between Modules
通过编写 "羞怯的"代码并应用得墨忒耳法则来避免耦合。

37.要配置,不要集成
Configure, Don"t Integrate
要将应用的各种技术选择实现为配置选项,而不是通过集成或工程方法实现。

38.将抽象放进代码,细节放进元数据
Put Abstractions in Code, Details in Metadata
为一般情况编程,将细节放在被编译的代码库之外。

39. 分析工作流,以改善并发性
Analyze Workflow to Improve Concurrency
利用你的用户的工作流中的并发性。

40.用服务进行设计
Design Using Services
根据服务----独立的、在良好定义、一致的接口之后的并发对象----进行设计。

41.总是为并发进行设计
Always Design for Concurrency
容许并发,你将会设计出更整洁、具有更少假定的接口。

42.视图与模型分离
Separate Views form Models
要根据模型和视图设计你的应用,从而以低廉的代码获取灵活性。

43.用黑板协调工作流
Use Blackboards to Coordinate Workflow
用黑板协调完成不同的事实和因素,同时又使各参与方保持独立和隔离。

44.不要靠巧合编程
Don"t Program by Coincidence
只依靠可靠的事物。注意偶发的复杂性,不要把幸运的巧合与有目的的计划混为一谈。

45.估计你的算法的阶
Estimate the Order of Your Algorithms
在你编写代码之前,先大致估算事情需要多长时间

46.测试你的估计
Test your Estimates
对算法的数学分析并不会告诉你每一件事情。在你的代码的目标环境中测定他的速度。

47.早重构,常重构
Refactor Early, Refactor Often
就和你会在花园里除草、并重新布置一样,在需要时对代码进行重写、重做和重新架构。要铲除问题的根源。

48. 为测试而设计
Design to Test
在你还没有编写代码时就还是思考测试问题。

49.测试你的软件,否则你的用户就得测试
Test Your Software, or Your Users Will
无情地测试。不要让你的用户为你查找bug。

50. 不要使用你不理解的向导代码
Don"t Use Wizard Code You Don"t Understand
向导代码可以生成大量代码。在你把它们合并进你的项目之前,确保你理解全部这些代码。

51.不要搜集需求----挖掘它们
Don"t Gather Requirements - Dig for Them
需求很少存在于表面上。它们深深地埋藏在层层假定、误解和政治手段下面。

52. 与用户一同工作,像用户一样思考
Work with a User to Think Like a User
要了解系统实际上将如何被使用,这是最好的方法。

53.抽象比细节活得更长久
Abstractions Live Longer than Details
"投资"于抽象,而不是现实。抽象能在来自不同的现实和新技术的变化的"攻击"之下存活下去。

54.使用项目词汇表
Use a Project Glossary
创建并维护项目中使用的专用术语和词汇的单一信息源。

55.不要在盒子外面思考----要找到盒子
Don"t Think Outside the Box - Find the Box
在遇到不可能解决的问题时,要确定真正的约束。问问你自己:"它必须以这种方式完成吗?它真的必须完成吗?"

56.等你准备好在开始
Start When You"re Ready
你的一生都在积累经验。不要忽视反复出现的疑虑。

57.对有些事情"做"胜于"描述"
Some Things are Better Done than Described
不要掉进规范的旋涡----在某个时刻,你需要开始编码。

58. 不要做形式方法的奴隶
Don"t Be a Slave to Formal Methods
如果你没有把某项技术放进你的开发实践和能力的语境中,不要盲目地采用它。

59.昂贵的工具不一定能制作出更好的设计
Costly Tools Don"t produce Better Designs
小心供应商的炒作,行业教条、以及价格标签的诱惑。要根据工具的价值判断它们。

60. 围绕功能组织团队
Organize Teams Around Functionality
不要把设计师与编码员分开,也不要把测试员与数据建模员分开。按照你构建代码的方式构建团队。

61.不要使用手工流程
Don"t Use Manual Procedures
Shell脚本或批文件会一次次地以同一顺序执行同样的指令。

62.早测试,常测试,自动测试
Test Early. Test Often. Test Automatically
与呆在书架上的测试计划相比,每次构建时运行的测试要有效的多。

63. 要通过全部测试,编码才算完成
Coding didn"t Done Until All the Tests Run
就是这样。

64. 通过"蓄意破坏"测试你的测试
Use Saboteurs to Test Your Testing
在单独的软件副本上故意引用 bug,以检验测试能够抓住它们。

65.测试状态覆盖,而不是代码覆盖
Test State Coverage, Not Code Coverage
确定并测试重要的程序状态。只是测试代码行是不够的。

66.一个bug只抓一次
Find Bugs Once
一旦测试员找到一个bug,这应该是测试员最后一次找到它。此后自动测试应该对应其进行检查。

67.英语就是一种编程语言
English is Just a Programming Language
像你编写代码一样编写文档:遵守DIY原则、使用原数据、MVC、自动生成,等等。

68.把文档建在里面,不要拴在外面
Build Documentation In, Don"t Bolt It On
与代码分离的文档不太可能被修整和更新。

69. 温和地超出用户的期望
Gently Exceed Your Users" Expectations
要理解你的用户的期望,然后给他们的东西要多那么一点。

70. 在你的作品上签名
Sign Your Work
过去时代的手工艺人为能在他们的作品上签名而自豪。一夜应该如此。

检查清单

71.要学习的语言
厌倦了C、C++和JAVA?试试CLOS、Dylan、Eiffel、Objective C、Prolog、Smalltalk或TOM。他们每一种都有不同的能力和不同的"风味"。用其中的一种或多种语言在家里开发一个小项目。

72.WISDOM 离合诗
What do you want them to learn?                       你想让他们学到什么?
What is their interest in what you"ve got to say?    他们对你讲的什么感兴趣?
How sophisticated are they?                           他们有多富有经验?
How much detail do they want?                         他们想要多少细节?
Whom do you want to own the information?              你想要让谁拥有这些信息?
How can you Motivate them to listen to you?           你如何使他们听你说话?

73.怎样维持正交性
设计独立、良好定义的组建。
使你的代码保持解藕
避免使用全局数据
重构相似的函数

74.应制作原型的事物
构架
已有系统的新功能
外部数据的结构或内容
第三方工具或组建
性能问题
用户界面设计

75.架构问题
责任是否得到了良好定义?
写作是否得到了良好定义?
耦合是否得以最小化?
你能否确定潜在的重复?
接口定义和各项约束是否可以接受?
模块能否在需要时访问所需数据?

76.调试检查清单
正在报告的问题是底层bug的直接结果,还是只是症状?
Bug 真的在编译器里?在OS里?或者是在你的代码里?
如果你向同事详细解释这个问题,你会说什么?
如果可以代码通过了单元测试,测试是否足够完整?如果你用该数据单元测试,会发生什么?
造成这个bug的条件是否存在于系统的其他任何地方?

77.函数的得墨忒耳法则
某个对象的方法应该值调用属于以下情形的方法:
它自身
传入的任何参数
它创建的对象
组件对象

78.怎样深思熟虑地编程
总是意识到你在做什么
不要盲目地编程
按照计划行事
依靠可靠的事物
为你的假定建立文档
不要只是测试你的代码,还要测试你的假定
为你的工作划分优先级
不要做历史的奴隶

79.何时进行重构
你发现了对DRY原则的违反
你发现事物可以更为正交
你的知识扩展了
需求演变了
你需要改善性能

80.劈开戈尔迪斯结
在解决不可能解决的问题时,问问自己:
有更容易的方法吗?
我是在解决正确的问题吗?
这件事情为什么是一个问题?
是什么使它如此难以解决?
它必须以这种方式完成吗?
它真的必须完成吗?

81.测试的各个方面
单元测试
集成测试
验证和校验
资源耗尽、错误及恢复
性能测试
可用性测试
对测试自身进行测试

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值