《程序员修炼之道》笔记(二)

第二章 注重实效的途径

1. 重复的危害

a) DRY-Don’t Repeat Yourself。系统中的每一项知识都必须具有单一、无歧义、权威的表示。

b) 重复是怎样发生的

Imposed Duplication强加的重复。开发者觉得他们无可选择-环境似乎要求重复。

Inadvertent Duplication无意的重复。开发者没有意识到自己在重复信息。

Impatient Duplication无耐心的重复。开发者偷懒,因为重复似乎更容易。

Interdeveloper dumplication开发者之间的重复。同一个团队的几个人重复了同样的信息。

下面是对这四类重复的详细解释

c) 强加的重复

1) 注释。糟糕的代码才需要许多注释。要把低级的知识放在代码中,把注释保留给其它高级的说明,否则过多的注释只是在重复知识,每次改变代码,注释也需要更改,最终注释会变得过时,不可信任的注释比完全没有注释更糟。可以考虑用合理的变量命名、逻辑清晰的代码逻辑来代替低级的注释,而描述函数运作原理的注释,以及约定函数的输入、输出等,这些应该算是高级注释。

2) 文档与代码。撰写文档,然后编写代码,文档和代码在重复同样的知识,文档需要与代码保持同步,但常常得不到及时的维护。这种情况估计执行力不到位的公司都会遇到。

d) 无意的重复。这常常来自不合理的设计。比如一条线段,设计了起点、终点两个属性后,如果再加上长度属性,便是多余的。

e) 无耐心的重复。这种重复最容易检测,为了走捷径而简单复制,常常是欲速而不达,一旦需要修改代码,这种简单地复制的行为就会受到应有的惩罚。

f) 开发者之间的重复。这类重复最难检测,项目在演进过程中,随着人员的变动,方案的调整,到最后往往很难看清项目的全貌,也许正在编写的函数已经实现过了却没人能想起来。对于这类重复,最好是通过清晰的设计、强有力的技术项目领导、明确的责任划分来规避。



2. 正交性

“正交性”本是本意是指几何中相互垂直的两条直线,正交时某个点沿着一条直线移动,它投影在另一条直线的位置不变。在软件领域中,正交性指某种不相依赖或解耦性。如果一个软件模块发生变化,不会影响其它模块,那它们就是正交的。要尽量设计内聚的组件(独立,具有单一、良好定义的目的)。

a) 软件模块正交的好处

1) 提高生产率

使改动局部化,降低开发测试时间

促进复用。如果组件具有明确而具体的、良好定义的职责,就可以用最初的开发者未曾想象过的方式,把它们与新组件组合起来。

充分发挥模块的功能,A组件M件事,B组件N件事,如果A B正交,可以组合成M*N种功能,这是最大化的。可能只一点只能体现在理论上吧。

2)降低风险

正交的设计可以隔离有问题代码区域,如果某个模块有问题,在正交系统中,不会蔓延到其它模块,要更换问题模块也很容易

让系统更健壮。对某个模块的改动,所导致的任何影响都被局限在该区域内。

更方便测试,因为设计测试、并针对其组件运行测试更容易,否则为了测试一个模块还要关联测试其它模块,这就像之前单元测试描述的,复杂度会快速膨胀。

3) 不会与某个特定的供应商、产品或平台绑在一起。但如果使用的是UI控件、ORM框架,要不绑在一起估计很困难。

b) 在工作中运用正交原则的几种方式

1) 项目团队。怎样把团队划分为责任互不重叠的小组,这个没有明确的答案,据项目而定,但可以从基础设施与应用分离开始。比如按照主要的基础设施组件(数据库、通信接口、中间件等)划分,并根据具体情况进行调整。对团队的正交性衡量有一个方法:查看在讨论每个所需改动时涉及的人数,人数越多正交性越差

2) 设计。采用分层的方法是设计正交系统的强大方式。每层都只使用在其下面的层次提供的抽象,在改动一个层的实现时,可以不影响其他层,拥有极大的灵活性。而且分层也降低了模块间依赖关系失控的风险,否则根本无法驾驭模块间的互相引用。衡量设计好坏,可以考虑这个问题:如果我显著地改变某个特定功能背后的需求,有多少模块会受影响?在最理想的正交系统中,答案应该是“一个”,现实中虽然很少能做到这样,但也应该是越少越好。而且要小心地作出假设,不要依赖你无法控制的事物,比如将电话号码作为顾客的识别码

3) 编码。要努力地让代码保持解耦。作者形象地比喻为:编写“羞涩”的代码。羞涩的代码不会没有必要地向其他代码模块暴露任何事情、也不依赖其他模块的实现。此外避免重复、应对变化是设计模式的拿手好戏,需要多学习领会

4) 测试。正交地设计和实现的系统更易于测试。建议每个模块都有自己的、内建在代码中的单元测试,并让这些测试作为常规构建过程的一部分自动运行。而且构建单元测试本身就是对正交性的有趣测试,如果为了构建某个单元测试,你需要把系统中其余部分拉进来,那么正交性就很差。



3. 可撤销性

a) 如果某个想法是你唯一的想法,再没有什么比这更危险的事情了。

b) 没有什么永远不变,而如果你严重依赖某一事实,你几乎可以确定它将会变化。要把决策视为写在沙滩上的,而不要把他们刻在石头上。大浪随时可能到来,把他们抹去。

c) 除了保持代码的灵活性,还需要考虑架构、部署及供应商等领域的灵活性。



4. 曳光弹

a) 在机枪射击中,常会把曳光弹与常规弹药交错装在弹药带上,发射时曳光弹会在枪与击中的地方留下烟火般的踪迹,而如果曳光弹击中目标,常规弹药也会击中目标。在软件开发中,如果有新的项目是你从未构建过,客户也没有用过类似系统以致需求模糊不清时,可以使用类似的曳光弹方法。

1) 曳光弹与真正的子弹在相同的环境和约束下工作,枪手能够得到即时的反馈。在软件开发中,使用曳光代码可以快速、直观可重复地从需求出发,满足最终系统的要求。

2) 曳光代码并非用过就扔的代码,它含有任何一段产品代码都拥有的完整的错误检查、结构、文档、自查,只是功能不全而已。曳光开发与项目永不会结束的理念是一致的:总有改动需要完成,总有功能需要增加,这是一个渐进的过程。

b) 曳光代码的优点

1) 用户能够及早看到能工作的东西,并帮你定位目标

2) 曳光代码相当于一个有待壮大的集成平台,一旦新的代码段通过了单元测试,就可以将它加入该环境中

3) 有了用于演示的东西

4) 将更能感觉到工作进展,相当于把一个大目标分成了许多小目标来完成

c) 但是曳光弹并非总能击中目标,曳光代码也不是总能满足需求,这正是曳光弹和曳光代码的价值所在。曳光代码可以帮助在客户的不断反馈中接近目标,而小段代码的惯性也小,改变起来容易、迅速

d) 曳光代码与原型的区别。原型制作的是用过就扔的代码,而曳光代码虽然简约,却是完整的,并且构成了最终系统的骨架的一部分。可以把原型制作视为在第一发曳光弹发射之前进行的侦查和情报搜集工作。



5. 调试

a) 调试的“心理学”

最容易欺骗的人是自己

不要恐慌

如果见到Bug的第一反应是“那不可能”, 就完全错了。不要浪费时间在以“那不可能”起头的思路上,因为那不仅可能,而且已经发生了。

在调试时小心近视,要抵制只修正你看到的症状的迫切愿望,要尽可能找到其它相关的地方,找出问题的根源,而不是问题的特定具体表现。

测试时尽可能覆盖全部边界条件。

b) 跟踪。如果需要观察程序或数据结构随时间的变化情况,就需要用到跟踪的方法。比如并发编程、实时系统、基于事件的应用中,将跟踪信息打印到屏幕或文件中就是有效的方法。

c) 审视自己的代码,看看是否有一些不严密的假定

欢迎关注我的个人公众号【菜鸟程序员成长记】

时间: 2024-10-06 03:06:56

《程序员修炼之道》笔记(二)的相关文章

《OC疯狂讲义》笔记(二)

1.NSString的使用 C的字符串保存:1)字符数组      2)字符串的指针 OC中又专门的字符串处理的类(有字符串类型) NSString  不可变的字符串 NSMutableString 可变字符串 NSString 是OC字符串的类 1) NSString保存字符串 NSString *str = @"abc";    //用str保存字符串常量 创建空字符串 NSString *str = [NSString new]; str = @"xxxx";

《OC疯狂讲义》笔记(一)

1.OC简介 Objective-C    继承自C和smalltalk 最小程度增加了面向对象的部分,是一门面向对象编程语言 1986. Next 得到授权 关于 Cocoa 框架(96) Cocoachina.com code4app.com 2.OC HelloWorld OC的文件有哪几种? .h  头文件(声明类) .m  OC源文件(类的实现) #import <Foundation/Foundation.h>  //导入文件 //main函数是OC得主入口函数 int main(

《OC疯狂讲义》笔记(三)

1.类方法 1)什么是类方法 对象方法: -(返回值类型)方法名:(形参的类型) 形参名; 类方法 +(返回值类型)方法名:(形参的类型) 形参名; 2)类方法怎么去定义 +(返回值类型)方法名:(形参的类型) 形参名; 3)类方法的使用 调用: 对象方法:  [对象名    方法名:实参] 类方法:    [类名      方法名:实参] 4)使用类方法的好处 1)提高效率 2)减少内存空间占用 3)代码更加简洁 缺点:不能访问对象的成员变量 5)对象方法和类方法的对比 类方法        

《OC疯狂讲义》笔记(四)

1.两个关键字:self 和 super self 可以用在对象和类方法中 1)self用在对象方法中:指代的时调用当前对象方法的那个对象 2)self用在类方法中:指代的时当前类(实质是类对象) 总结:当self在方法中使用,谁调用这个方法,self指代的就是谁 super: 使用super的地方,一定存在继承关系 super调用父类的方法 2.面向对象的其他特性:继承 继承概念: 假设有两个类:A   B 继承是两个类之间的关系 假设声明B类的时候, 1)导入父类的头文件 2)设定继承关系

《OC疯狂讲义》笔记(五)

1.OC中的点语法 点语法: 对象名.成员变量名   (点语法不是访问成员变量,而是方法的调用) 它是一个编译器特性 点语法作用:可以替换传统的get和set方法的调用 点语法的前提: 首先得先有 get和set方法 点语法实质: 调用get和set方法 如果点语法出现在 = 的左边,表示设置值,相当于调用了set方法 p.age = 10; 替换为:[p setAge:10]; 如果点语法出现在 = 的右边,表示获取值,相当于调用了get方法 int age = p.age; 替换为:int

李刚OC语言疯狂讲义笔记

设计一个”学生“类1> 属性* 姓名* 生日用结构体作为类的实例变量(生日) #import <Foundation/Foundation.h> //定义生日的结构体 typedef struct{ int year; int month; int day; }MyDate; @interface Person : NSObject { @public NSString *_name;//定义姓名 MyDate _birthday;//定义生日 } @end @implementatio

java疯狂讲义笔记整理(第二版第一部分)

第一章    java语言概述 1.1    java语言的发展简史 1990末:    sun公司“Green计划”(James Gosling领导)        ----目的是智能家电编写一个通用嵌入式控制系统,为此创建oak 1992夏天:    "Green计划"完成新平台的部分功能 1992年11月:    "Green计划"被转为"FirstPerson有限公司"-一个sun的全资子公司,致力于创建一个高度互动的设备 1994夏天:

java8--类加载机制与反射(java疯狂讲义3复习笔记)

本章重点介绍java.lang.reflect包下的接口和类 当程序使用某个类时,如果该类还没有被加载到内存中,那么系统会通过加载,连接,初始化三个步骤来对该类进行初始化. 类的加载时指将类的class文件读入内存,并为之创建一个java.lang.class对象,也就是说,当程序中使用任何类时,系统都会为之建立一个java.lang.Class对象.(几乎所有的类都是java.lang.Class的实例); 所以JVM最先初始化的总是java.long.Object类. 在java中,一个类用

疯狂html5讲义(二):HTML5简的常用元素与属性(一):html5保留的常用元素

html5删除了少量的元素与属性:主要删除了文档样式相关的各种元素与属性,比如<font>.width等,html5规范推荐使用css样式单来控制html文档样式. 1.基本元素 <html>.<head>.<title>.<body>.<style>.<h1>到<h6>.<p>.<br>.<hr>(定义水平线).<div>.<span>~~~ <

疯狂html5讲义(二):HTML5简的常用元素与属性(二):html5新增的通用属性

1.contentEditable属性 true时可直接编辑html元素里面的内容,且具有"可继承"的特点. 编辑后不要刷新页面,否则编辑的内容会丢失,可以通过该元素的innerHtml属性来获取编辑后的内容. 2.designMode属性 此属性相当于一个全局的contentEditable属性 3.hidden属性 可以替代css中的display,hidden=true相当于display:none 4.spellcheck属性 针对于input.textarea等元素,可以对用