注重实效的哲学
编程是一种技艺,一种需要用心学习的技艺
谢谢你们让我们梦想!
注重实效的程序员的特征:
- 早期的采纳者/快速的改编者:技术和技巧上的直觉
- 好奇:喜欢提问
- 批判的思考者:首先抓住事实而不是照搬别人的说法
- 有现实感:设法理解你面临的每个问题的本质
- 多才多艺:尽力熟悉广泛的技术和环境
Care about your craft!
Think! about your work!
在所有的弱点,最大的弱点就是害怕暴露弱点。 —- J.B.Bossuet
软件系统的熵(entropy)表征了这个系统的混乱程度,指的是这个系统的“无序”的总量,也就是 不确定性程度。
提示4:
Dont Live with Broken Windows
不要留着“破窗户”(低劣的设计、错误决策、或是糟糕的代码)不修,发现一个修理一个。
如果你发现你所在团队和项目的代码十分漂亮 — 编写整洁、设计良好,并且很优雅,你就很可能会格外注意不去把它弄脏。即使有火在咆哮(最后期限、发布日期、会展演示,等等),你也不会想成为第一个弄脏东西的人。
提示6:
Remember the big picture
需要留心大图景。要持续不断的观察周围发生的事情,而不只是你自己在做的事情。—-温水煮青蛙的故事。
知识上的投资总能得到最好的回报。 —-本杰明.富兰克林
随着你的知识的价值降低,对你的公司或客户来说,你的价值也在降低。我们想要阻止这样的事情,决不让它发生。
知识资产(Knowledge Portfolios)的保值:
- 定期投资:作为习惯
- 多元化:你知道的不同的事情越多,你就越有价值。作为底线,你需要知道你目前所用的特定技术的各种特性。
- 管理风险:高风险,高回报,低风险,低回报;不应太保守,错过可能的机会。不要把所有的技术鸡蛋放在一个篮子里。
- 低买高卖:新兴技术流行之前学习它可能就和我们找到被低估的股票一样。
- 重新评估和平衡:IT行业是一个很动荡的行业,你需要重新评估和平衡你现在掌握的技术。
提示8:
Invest Regularly in Your Knowledge Portfolio 定期为你的知识资产投资
- 每年至少学习一种新语言。拓展思维,避免墨守成规。
- 每季度阅读一本技术书籍。掌握你现在使用的技术,扩宽范围,阅读一些与你项目无关的书籍。
- 阅读相关非技术书籍。提高自己的人文素养。
- 试验不同的生产环境。适应变化。
- 参加本地用户的组织。融入当地。
提示9:
Critically Analyze What You Read and Hear 批判地分析你读到的和听到的
不要低估商业主义的力量。web搜索引擎的页面前面的结果,并不意味着那就是最佳选择;内容供应商可以付钱排在前面。交流越有效,你就越有影响力。
注重实效的途径
提示11:
DRY -Don’t Repeat Yourself
系统中的每一项知识都必须具有单一、无歧义、权威的表示。
重复是如何发生的:
- 强加的重复(imposed duplication):开发者觉得无可选择—环境似乎要求重复
- 无意的重复(inadvertent duplication):开发者没有意识到他们在重复
- 无耐性的重复(impatient duplication):开发者偷懒,重复因为更容易
- 开发者之间的重复(interdeveloper duplication):同一团队不同人重复了同样的信息
提示12:
Make it Easy to Reuse 让复用变得容易
满足正交性,在计算机技术中,该术语用于表示某种不相互依赖性或解耦性。
比如:改动界面,而不影响数据库;而更换数据库,不用更改界面。
当任何系统的各个组件相互高度依赖时,就不再有局部修正(local fix)这样的事情。
面向对象程序设计中的 “高内聚、低耦合”
使用若干技术维持正交性:
- 让你的代码保持解耦 —> 面向对象中的迪米特法则
- 避免使用全局数据 —> 引用全局数据使得把自己与共享该数据的其他组件绑定在了一起
- 避免编写相似的函数 —> 开始和结束共享公共的代码,中间的算法缺不相同。重复的代码是结构问题的一种症状:使用设计模式中的策略模式。
提示15:
Use Tracer Bullets to Find the Target 使用曳光弹找到目标
曳光弹类比,曳光弹与真实的子弹在相同的环境、相同的约束下工作。它们快速飞向目标,枪手因此可以得到及时的反馈信息,从而快速调整。 —> 使用曳光型代码构建解决方案。
曳光代码的优点:
- 用户能够及早看到工作的东西。
- 开发者构建了一个他们能在其中工作的结构。
- 你有了一个集成平台。
- 你有了可用于演示的东西。
- 你将更能够感觉到工作进度。
曳光代码虽然简约,但却是完整的,并且构成了最终系统的骨架的一部分,并不是用过即扔的代码。可以把原型制作视为在第一发曳光弹之前进行的侦查和情报收集工作。
基本工具
一个程序员是一位 工匠,也需要有工匠这种的心态,每一位工匠都需要一套品质良好的基本工具,而这些工具需要经过认真挑选,可以完成很少与其他工具重合的特定工作。
纯文本(Plain Text)— 最强大的工具之一
纯文本包含txt、xml、html等,一些是有结构的如xml,一些是没有结构的如txt
提示20:
Keep Knowledge in Plain Text 用纯文本保存知识
纯文本的好处:
- 保证不过时:人能够阅读的数据形式,以及自描述的数据,将比所有其他的数据形式和创建它们的应用都活的更长久,数据格式让人们能够理解(human understandable)。
- 杠杆作用:如果文件是纯文本格式,可以使用版本控制管理系统进行管理,可以自动保存所有改动的历史。从另一方面说纯文本保留着通往二进制的接口。
- 更易于测试:如果测试文件是纯文本格式,那么增加、更新、或是修改测试数据就是一件很简单的事情。
使用脚本语言和编辑器
提示21:
Use the Power of Command Shells 利用命令shell的力量
在Linux下可以使用shell命令操作,查找各种文件,比单纯用GUI界面会方便许多,也更加高效
提示22:
Use a Single Editor Well 用好一种编辑器
选一种编辑器,彻底了解并熟练掌握它,并将其用于所有的编辑任务:代码、文档、备忘录、系统管理等。
编辑器将成为 双手的延伸~~键会滑过文本和思想时歌唱起来!
我现在选择ATOM!编辑器就是信仰问题~
好的编辑器需要有的特性(ATOM都包含):
- 可配置:偏好配置,字体颜色,热键绑定等
- 可扩展:集成各种插件,可以集成各种新的语言编译环境
- 可编程:可对编辑器编程,让它执行复杂的、多步骤的任务。可以通过宏或内建的脚本编程语言进行编程控制该编辑器。
- 语法高亮
- 联想和自动缩进
- 初始代码模板设置
- 类IDE特性(编译、调试等等)
复现问题一个行之有效的方法:橡皮鸭的方式
也就是向别人解释你写的代码,她应该越过你的肩膀看着屏幕,不断点头(像澡盆里上下晃动的橡皮鸭一样),她们应该一个字也不需要说,你只是一步步解释代码要做些什么,常常就能让问题从屏幕上跳出来,宣布bug自己的存在。
提示27:
Don’t Assume it — Prove It,不要假定,要证明。
遇到问题,要想想自己的代码有无问题,而不是怀疑环境、系统自身的问题。
提示28:
Learn a Text Mainpulation Language 学习一种文本操作语言
比如常用的Python、Perl等语言,完全可以试试编写脚本优化自己的编程及提高处理效率。
构建自己的代码生成器
当工匠面临一再地重复制作同一样东西的任务时,他们会取巧,需要建造夹具或模板,一旦他们做好了夹具,他们就可以反复制作工件。夹具带走了复杂性,降低了出错的机会。
程序员往往也处在这样的位置上,也需要构建 代码生成器,一旦构建好,在整个项目生命周期都可以使用它。
代码生成器主要有两种主要类型:
- 被动代码生成器:生成的结果与生成器分离,生成结果独立。
- 本质上是参数化模板,根据一组输入生成给定的输出
- 可以生成模板、源码控制提示、版权说明及标准注释块等
- 主动代码生成器:每次需要结果时使用,结果用过就扔。
- 将伪代码生成对应的可以运行的代码就需要使用主动代码生成器
- 不同语言代码之间的转换也需要主动代码生成器
注重实效的偏执(Pragmatic Paranoia)
编码过程我们必须不断地与他人的代码进行接合,这个代码可能不符合我们的高标准的代码,因此我们常常 “防卫性编码”
而注重实效的程序员会更进一步,他们往往自己也不信任,他们会针对 自己的错误 进行防卫性的编码。
当每个人都确实要对你不利时,偏执就是一个好主意。 —- Woody Allen
按合约设计(Design by Contract, DBC)
软件系统中使用按合约设计的一种方式,它关注的是用文档记录(并约定)软件模块的权利与责任,以确保程序的正确性。程序的正确性体现在完成了它声明要做的事情的程序,而DBC的核心所在就是用文档记载这样的声明,并进行校验。
合约包含三个方面:
- 前条件(precondition):为了调用例程,必须为真的条件
- 后条件(postcondition):例程保证会做的事情,例程完成时世界的状态
- 类不变项(class invariant):类确保从调用者的视角来看,该条件总是为真
- 循环不变项:循环的最终目标的陈述,每次迭代都保持为真,可用来确定边界
- 语义不变项:表达不可违反的需求,一种“哲学合约”
使用DBC的最大好处也许是他迫使需求与保证的问题走到前台来。在设计时简单的列举输入域的范围是什么、边界条件是什么、例程允诺交付什么等等。
断言式编程
需要抛弃 “这绝不会发生” 这种概念或者固执,但如果你的程序预期不应该或者不可能走到某一分支,那就应该使用断言式编程。
提示33:
If It can’t Happen,Use Assertions to Ensure That It Won’t 如果它不可能发生,用断言确保它不会发生
但传给断言的条件不应该有副作用,断言可能会在编译时被关闭,绝不要把必须执行的代码放在assert中。
不要用断言代替真正的错误处理,断言检查的是绝不应该发生的事情。
使用异常进行错误处理
异常很少应作为程序的正常流程的一部分使用,异常应保留给意外事件。
异常表示即时的,非局部的控制转移 —-这是一种级联的(cascading)goto
提示34:
Use Exceptions for Exceptional Problems 将异常用于异常问题
如何配平资源
只要在编程,我们都要管理资源:内存、事务、线程、文件、定时器 —- 所有数量有限的事物。
大多数时候,资源使用遵循一种可预测的模式:你分配资源、使用它,然后解除其分配。
提示35:
Finish What You Start 要有始有终
这意味着,分配某项资源的例程或者对象应该负责解除该资源的分配。
比如,打开文件和关闭文件应在同一个函数或者例程中实现,一个函数打开该文件并在退出时关闭它。
嵌套分配资源:
- 以与资源分配的次序 相反的次序解除资源的分配。这样如有一个资源含有对另一个资源的引用,就不会造成资源的遗弃和泄漏
- 在代码的不同地方分配同一组资源,总是以相同的次序分配它们,这将降低死锁的可能性。
无法配平资源时(出现动态数据结构的程序中),有三种选择(三选一,一般由所选语言决定): - 顶层结构还负责释放它的任何子结构。
- 只是解除顶层结构的分配。它指向的(没有在别处引用的)任何结构都会被遗弃。
- 如果顶层结构含有任何子结构,它就拒绝解除自身的分配。
弯曲,或折断(Bend, or Break)
生活不会停步不前,我们的代码也不会。为了让我们赶上今天近乎疯狂的变化步伐,我们须要尽一切努力编写尽可能宽松、灵活的代码(因此我们需要设计模式,选择合理的设计模式对于代码
复用将带来较大的正向作用)。
提示36:
Minimize Coupling Between Modules 使得模块之间的耦合减至最少
也就是说编写代码的时候尽量遵循设计模式,比如迪米特法则,一个软件实体应当尽可能少地与其他实体发生作用。
迪米特法则规定,任何方法的调用都应该只调用属于以下情况的方法:
- 它自身
- 传入该方法的任何参数
- 它创建的任何对象
- 任何直接持有的组件对象
遵循迪米特法则缩小了调用类中的响应集(response set)的规模,以这种方式设计类的错误也往往更少。
响应集:类的各个方法直接调用的函数的数目。
元程序设计
再多的天才也无法胜过对细节的关注 — Levy Eighth Law
细节会弄乱我们的代码,如果它们经常发生变化的话。每当我们改动代码,我们都可能会引入新的bug,因此我们可以让代码编的高度可配置化,也就是,容易适应变化。一个可用的方法使将细节封装至元数据。
提示37:
Configure, Dont Intergrate 要配置而不要集成
使用元数据(metadata)描述应用的配置选项,元数据是关于数据的数据,可以理解为元数据是任何对应用进行描述的数据 —-应用该怎么运行、它应该使用什么资源。通常元数据是在运行时访问和使用,而不是在编译时访问和使用。
建议使用纯文本的方式配置元数据。
通常许多的程序只在启动时读取配置,如果修改配置,需要重新启动应用。更为灵活的方法使编写能在运行时重新加载其配置的程序。
- 协作式配置:让应用相互的配置,让其适应其环境的软件,这就是动态地配置。
时间耦合
时间耦合是软件架构中常常被忽略的概念,我们通常都是线性的思考,先做这个,然后再做那个。但软件中这种运行方式将带来时间耦合。
时间的要素
- 并发:事情在同一时间发生
- 次序:事情在时间中的相对位置
时间耦合解决的就是并发的难题,我们需要容许并发,并解除时间或次序上的依赖,任何时间上的依赖一般包含以下几个方面,在这几个方面改善并发性,减少时间耦合:
- 工作流分析:UML活动图,分析工作流,改善并发
- 架构分析:饥饿的消费者模型,用服务进行设计—定义良好的、一致接口之后的独立、并发的对象
- 设计分析:对任何全局或静态变量加以保护,免于并发访问;使用更整洁的接口
- 部署分析:把系统架构成多个独立的服务,让配置成为动态的
当你编码时
编码不是机械的工作。每一分钟都需要决策,如果要让所得的程序享有长久、无误和富有生产力的一生,就必须对这些决策进行仔细的思考和判断。
编写代码时,要让代码易于测试。
靠巧合编程
作为开发者,每天都处在雷区,记住士兵的故事,不要做出错误的结论,应该避免靠巧合编程 —不依靠运气和偶然的成功,要深思熟虑地编程。