《代码大全》章节试读

当前位置:首页 > 网络编程 > > 代码大全章节试读

出版社:电子工业出版社
出版日期:2006-12
ISBN:9787121033629
作者:迈克康奈尔
页数:914页

《代码大全》的笔记-软件构建中的设计 - 软件构建中的设计

软件开发中任何其他技术目标都不如管理复杂度重要。

《代码大全》的笔记-第119页 - 设计的层次

软件系统
分解为子系统或包
比如数据库、用户界面、业务规则、命令解释器、报表引擎。
不同子系统之间相互通信的规则。如果所有的子系统都能同其他子系统通信,你就完全失去了把它们分开所带来的好处。应该限制子系统之间的通信。
热力学第二定律。每一个自发的物理或化学过程总是向著熵(entropy)增高的方向发展。熵增加。
无环图。
常用子系统。
业务规则。
用户界面。
数据库访问。
对系统的依懒性。
为什么一定要把自己局限于 Windows 的环境呢?把所有与 Windows 相关的系统调用都隔离起来,放到一个 Windows 接口子系统中。
分解成类。
分解成子程序。
子程序内部的设计。

《代码大全》的笔记-11.1 Consideration in Choosing Good Names - 11.1 Consideration in Choosing Good Names

Computed-Value Qualifiers in Variable Names
如果你要用类似于Total、Sum、Average、Max、Min、Record、String、Pointer这样的限定词来修饰某个名字,那么请记住把限定词加到名字的最后。
例外:Num限定词的位置已经是约定俗成的。Num放在变量名的开始位置代表一个总数:numCustomers;Num放在变量名的结束位置代表一个下标:customerNum表示当前员工的序号。
最好是使用Count和Total来代表员工的总数,用Index来指代某个特定的员工。

《代码大全》的笔记-第89页

软件开发的核心是管理问题(软件)的复杂度。为此,功能的分解(模块化)和功能的抽象都是必要的途径。前者很多时候被应用在水平分割上,后者其实可以看做是垂直方向上的努力,但细究起来,应该还是相同的。恰当的分解必须要适当地抽象出子问题,正确的抽象需要对抽象对象的分解有清晰的认识。

《代码大全》的笔记-第125页 - 可以工作的类

Key Points:
1. 类的接口应该提供一致的抽象,很多问题都是因为违背这一原则引起的。
2. 类的接口应该隐蔽一些信息,——如某个系统接口,某项设计策略,或一些实现的细节。
3. 包含往往比继承更可取,除非是“is a”的关系模型。
4. 继承有用,但是增加了软件的复杂度,违背了软件技术的首要技术使命——管理复杂度。
5. 类是管理复杂度的有力工具,设计中应该给予足够的关注。

《代码大全》的笔记-第1页 - 一、前期准备

一、前期准备
1.需求:
发现错误的时间要尽量接近进入该错误的时间。
如果没有一个良好的问题定义,你努力解决的可能是一个错误的问题。
明确的需求有助于确保用户驾驭系统的能力;
明确的需求有助于避免争论。
重视需求有助于减少开始编程开发之后的系统变更情况。
需求像水。如果冻结了,就容易在上面开展建设。
开发过程能够帮助客户更好的理解自己的需求,这是需求变更的主要来源。
2.架构
架构应详细定义所需的主要的类。瞄准80/20准则:对构成系统的80%功能的20%的类进行详细说明。
架构应建立一套有关错误消息的约定。
架构的总体质量:
《人月神话》的中心论题,说的就是大型系统的本质问题是维持其“概念完整性”。
好的架构设计应该与待解决的问题和谐一致。
架构应该描述所有主要决策的动机。
优秀的软件架构很大程度上是与编程语言无关的。
3.构建决策
深入一种语言去编程
确定你在技术浪潮中的位置,并相应调整计划和预期目标。

《代码大全》的笔记-第187页

确保在其他人做出危险动作时你也不会受到伤害。你要承担起保护自己的责任,哪怕是其他人犯的错误。
Programmer is a tiny god. 构建软件的过程本身,类似于构建一个世界。在“充满非法数据的冷酷世界”,最要紧的事情是杜绝所有能够出错的可能。怕处必有鬼。
本书有着非常囧的名字,内容并非可以拿来主义的代码片段集合,而是事无巨细的程序员成长法则。

《代码大全》的笔记-第80页 - 3.5 架构的先决条件

『架构』、[系统架构 / system architecture]、[高层设计 / high-level design]、[顶层设计 / top-level design]
离开了良好的软件架构,你可能瞄准了正确的问题,但却使用了错误的解决方案。也许完全不可能有成功的构建。
如果你不能向一个六岁儿童解释某件事,那么你自己就没有真正理解它。 —— 爱因斯坦

《代码大全》的笔记-第26页 - 三思而后行:前期准备

如果你的项目经理装成陆军准将的样子,命令你立刻开始写代码,你可以轻易的回答:“遵命!”(这又什么可怕的?老手当然知道 他在说什么。)这种回答很糟糕,你可以有几种更好的替换方案。首先,你可以断然拒绝以这种无效的命令,加入你和老板的关系不错,而且你银行账户的钱数也支持你这么做的话,祝你好运。
第二个不太靠得住的方案是假装在写代码,而事实上不是,在你的桌角上放一份旧的程序代码清单,然后投入需求和架构的开发中,同时不用去理会老板同不同意。你将能够更快的完成项目,并得到更高的质量。有些人会觉得这种不合乎伦理,但是从你老板的视角来看,无知是福。
第三种方法是,你可以教育你的老板,告诉他技术项目的微妙之处。这是一个好办法,因为它能增加世界上脱盲的老板的人数。下面的小节将继续讲述“在构建后动之前话时间做前期准备”的根本原因。
最后一个方案是,你可以另外找一份工作。虽然经济景气程度时高时低,但是优秀的程序员永远是紧缺的。人生苦短,当有大量更好的选择摆在你面前的时候,在一个荒蛮的软件企业中工作是不明智的。

《代码大全》的笔记-10.4 Scope - 10.4 Scope

Python中的循环下标变量和循环中定义的变量可以用于循环外。Python中最小范围的作用域是函数(局部作用域)。
尽可能缩短变量的跨度和“存活”时间
缩短变量跨度、“存活”时间和使用全局变量之间的权衡:开始时采用最严格的可见性,然后根据需要扩展变量的作用域。如果你无法把变量的作用域限定在对该变量承担最主要的责任的那个类里面,那么就创建一些访问器子程序来让其他类共享该变量的数据。这样你就会发现自己极少(如果有的话)需要使用赤裸裸的全局数据。

《代码大全》的笔记-第338页

需要使用全局数据的场景
之一:
消除流浪数据。有的时候你把数据传递给一个子程序或者类,仅仅是因为想要把它传递给另一个子程序或者类。这样的对象被称为“流浪数据(tramp data)”。使用全局变量可以消除流浪数据。

《代码大全》的笔记-第73页 - 问题定义的先决条件

如果『框框』是约束和条件的边界,那么诀窍在于找到这个『框框』。不要在『框框』之外思考 —— 找到这个『框框』。 —— Andy Hunt 和 Dave Thomas。
在开始构建之前,首先要解决的一项先决条件是,对这个系统要解决的问题做出清楚的陈述。『产品设想 / product vision』,『设想陈述 / vision statement』,『任务陈述 / mission definition』,『产品定义 / product definition』,『问题定义 / problem definition』。
问题定义在具体的需求分析之前,需求分析是对所定义的问题的深入调查。
问题定义应该用客户的语言来书写,而且应该从客户的角度来描述问题。通常不应该用计算机的专业术语叙述。最好的解决方案未必是一个计算机程序。
如果没有良好的问题定义,你努力解决的可能是一个错误的问题。
在射击之前,确信你瞄准了正确的目标。
『未能定义问题』的处罚是,你浪费了大量时间去解决错误的问题。这是双重处罚,因为你也没有解决正确的问题。

《代码大全》的笔记-第4页

四、表驱动法
表驱动法是一种编程模式,从表里面查找信息而不使用逻辑语句(if和case)。
表提供了一种复杂逻辑和继承结构的替代方案。
五、一般控制方法
1.布尔表达式
拆分复杂的判断而引入新的变量;
把复杂的表达式做成布尔函数;
用决策表代替复杂的条件。
2.按照数轴的顺序编写数值表达式;
3.将复杂度降低到最低是编写高质量代码的关键。
六、代码改善
1.软件质量的普遍原理就是改善质量以降低开发成本。
2.提高生产效率和改善质量的最佳途径就是减少花在代码返工上的时间,无论返工是由需求、设计改变还是调试引起的。
3.结对编程,通过复查可以快速地将所有开发者的水平提高到最高优秀的开发者的高度。

《代码大全》的笔记-第720页 - 十、代码调整技术(性能)

十、代码调整技术(性能)
1.逻辑
* 在知道答案后停止判断;
* 按照出现频率调整判断顺序,让运行最快和判断为真可能性的判断首先执行;
2.循环
* 将判断外提。如果在循环运行时某个判断结果不会改变,你就可以把这个判断提到循环的外面。
* 合并。就是把两个对同一组元素进行循环的操作合并在一起,减少循环开销。
* 尽可能减少在循环内部做的事情。如果可以在循环外面计算某些语句,而只在循环内部使用计算结果,那么就把该部分语句放在循环外面。
十一、管理构建
1.鼓励良好的编码实践几种技术
* 给项目的每一部分分派两个人
* 逐行复查代码
* 要求高级技术人员给代码签名
* 安排一些好的代码示例给别人看
* 强调代码是公共财产
* 奖励好代码
* 一份简单的标准:“我必须能理解并阅读这个项目里的所有代码”
2.编程工具
①源代码工具
* IDE
* diff比较器
* merge工具
* 源代码美化器
* 代码模板
②重构源代码
重构器
程序库:
代码生成器
打造自己的编程工具

《代码大全》的笔记-第376页 - 第16章 控制循环

一个循环只做一件事 仅靠循环可同时做两件事的这一事实,是无法充分证明这两件事是应该放在一起做的。循环应该和子程序一样,每个循环只做一件事并且把它做好。如果用两个循环会导致效率底下,而使用一个循环很合适,那么就把代码写成两个循环,并注明可以把它们合并起来以提高效率,然后等测量数据显示程序的这一部分性能低下的时候再去合并它们。

《代码大全》的笔记-第124页 - 5.3 设计构造块:启发式方法

先别问系统做什么,问问它想模仿什么! —— Bertrand Meyer。
首选,面向对象。
辨识对象及其属性。
确定可以对各个对象进行的操作。
确定各个对象能对其他对象进行的操作。
确定对象的哪些部分能对其他对象进行的操作。
确定对象的哪些部分对其他对象可见 —— 哪些部分可以是公用的,哪些是私用的。
定义每个对象的公开接口。
抽象。
封装实现细节。

我该隐藏些什么。
具名常亮 MAX_EMPLOYEES 来隐藏 100。
找出容易改变的区域。
优秀的设计师所共有的一项特质就是都有对变化的预期能力。把不稳定的区域隔离出来,从而把变化所带来的影响限制在一个子程序、类或者包的内部。
找出看起来容易变化的项目。
把容易变化的项目分离出来。
把看起来容易变化的项目隔离出来。
保持松散耦合
一个模块越容易被其他模块所调用,那么它们之间的耦合关系就越松散。

《代码大全》的笔记-第57页 - 应用软件技术:智慧工具箱

好的工匠知道完成某项工作要用到哪样工具,也知道该怎样正确地使用。

《代码大全》的笔记-第460页 - 20

读了前面20章,大概记了一些软件构件以及开发过程中的注意事项。这本书还是需要读几遍。

《代码大全》的笔记-第17页

在开发一个软件产品时,原材料甚至更加廉价,但劳动力上的花销也更昂贵。变更一份报表的格式所要付出的代价,和移动房间里的一堵墙一样高昂,因为两者的主要成本构成部分都是花费人的时间。

《代码大全》的笔记-第620页 - 十、代码调整技术(性能)

十、代码调整技术(性能)
1.逻辑
* 在知道答案后停止判断;
* 按照出现频率调整判断顺序,让运行最快和判断为真可能性的判断首先执行;
2.循环
* 将判断外提。如果在循环运行时某个判断结果不会改变,你就可以把这个判断提到循环的外面。
* 合并。就是把两个对同一组元素进行循环的操作合并在一起,减少循环开销。
* 尽可能减少在循环内部做的事情。如果可以在循环外面计算某些语句,而只在循环内部使用计算结果,那么就把该部分语句放在循环外面。

《代码大全》的笔记-第750页 - 十二、源代码布局与风格

十二、源代码布局与风格
1.基本原则
* 好布局方案的关键是能使程序的外观与逻辑结构一致,也就是让人和计算机有同样的理解。
* 编程工作量的一小部分是写让计算机能看懂的程序,一大部分是让其他人能看懂程序。
* 外表悦目比其他指标是最不重要的。
2.良好布局的目标
* 程序表现代码的逻辑结构
* 始终如一的表现代码的逻辑结构
* 改善可读性
* 经得起修改
3.布局技术
* 空白。能提高可读性,如空格、制表符、换行、空行。
* 分组。确保相关的语句成组放在一起。
* 空行。将不相关的语句分隔开。
* 缩进。用缩进的形式显示程序的逻辑结构。
* 括号。对包含两个以上的项的表达式,应该用括号去澄清。
4.控制结构的布局
* 不要用未缩进的begin-end对
* 段落之间要使用空行
* 单语句代码段的格式要前后统一
* 对于复杂的表达式,将条件分隔放在几行上
5.单行语句的布局
* 每行只放一个语句
* 每个语句只进行一个操作
* 数据声明的布局:
*
* 每行只声明一个变量
* 变量声明应尽量接近其首次使用的位置
6.注释的布局
* 注释的缩进要与相应代码一致
* 每行注释至少用一个空行分开
7.子程序的布局
* 用空行分隔子程序的各部分
* 将子程序参数按照标准缩进
8.类的布局
类接口的布局
一个文件应只有一个类
文件的命名应与类名有关
在文件中清晰的分隔子程序,至少使用两个空行

《代码大全》的笔记-23.2 Finding a Defect - 23.2 Finding a Defect

Stabilize the Error
如果一个错误无法重现,这通常会是一个初始化错误,或者是一个同时间有关的问题,或者是悬空指针(dangling-pointer)问题。如果某个求和结果时对时错,很有可能是参与计算的某个变量未能正确的初始化——可能正好在大多数情况它是从0开始。如果这个问题诡异且变化莫测,并且你又在使用指针,那么几乎可以肯定是你的代码中有未初始化的指针,或者用了所指向的内存区域已经被释放的指针。Brute-Force Debugging
1. 对崩溃代码的设计和编码进行彻底检查
2. 抛弃有问题的代码,从头开始设计和编程
3. 抛弃整个程序,从头开始设计和编程
4. 编译代码时生成全部的调试信息
5. 在最为苛刻的警告级别中编译代码,不放过任何一个细微的编译器警告
6. 全面执行单元测试,并将新的代码隔离起来单独测试
7. 开发自动化测试工具,通宵达旦的对代码进行测试
8. 在调试器中手动的遍历一个大的循环,直到发现错误条件
9. 在代码中加入打印、显示和其他日志记录语句
10. 用另一个不同的编译器来编译代码
11. 在另一个不同的环境里编译和运行程序
12. 在代码运行不正确的时候,使用能够产生警告信息的特殊库或者执行环境来链接和运行代码
13. 复制最终用户的完整系统配置信息
14. 将新的代码分小段进行集成,对每段集成的代码段进行完整的测试
在使用“快速肮脏调试法”的时候设置一个时间上限。如果确定有能够解决问题的蛮力技术——如上所列,那么没必要花上两小时调试原本只用三十分钟就能写出来的代码。

《代码大全》的笔记-第80页

关于设计
第一点也是最重要的,避免复杂度。
要避免“聪明”的设计,因为“聪明”的设计往往是难以理解的。
维护、扩展、耦合、重用 都需要考虑,但是在做这些的时候都要避免过度的增加复杂度。
第二点,精简型。 伏尔泰说:一本书的完成,不在它不能加入更多内容的时候,而在它不能删去任何东西的时候。软件也是如此,这也是避免复杂性的一个做法,这样我们就可以不去理会那些本来可以删去的代码。
第三点,层次性,这个可以说是设计的模式与方法,不能说是原则,一般来说,系统都是按照层次来设计的。

《代码大全》的笔记-第92页 - 3.6 花费在前期准备上的时间

一个运作良好的项目会在需求、架构以及其他前期计划方面投入 10% ~ 20% 的工作量和 20% ~ 30% 的时间。

《代码大全》的笔记-第61页 - 3.1 前期准备的重要性

关注质量就是提高生产力的最佳途径。
使用高质量的实践方法是那些能创造高质量软件的程序员的共性。
方法论应该选用最新最好的,而不应该无知地做出选择。当然也应该公平地对待『旧且可靠的方法』。 —— Harlan Mills
准备工作的中心目标是降低风险:一个好的项目规划者能够尽可能早地将主要的风险清除掉,以使项目的大部分工作能够尽可能平稳地进行。目前,软件开发中最常见的项目风险是糟糕的需求分析和糟糕的项目计划,因此准备工作就倾向于集中改进需求分析和项目规划。
在实现一个系统之前,你需要理解『这个系统应该做什么』,以及『它该如何做到这些』。
作为技术雇员,你的一部分工作就是培训周围的非技术人员。
草图,表达设计概念。蓝图,设计详图,包含所有细节信息。
程序员是软件食物链的最后一环。架构师吃掉需求,设计师吃掉架构,而程序员则消化设计。
在构建活动开始之前清除一个错误,那么返工的成本仅仅是『在开发过程的最后阶段(在系统测试期间或者发布之后)做同样事情』的十分之一到百分之一。
发现错误的实践要尽可能接近引入该错误的时间。缺陷在软件食物链里面呆的实践越久,它对食物链的后级造成损害就越严重。
高度序列化。高度迭代型。

《代码大全》的笔记-第5页 - 开发者测试

七.开发者测试
1⃣白盒测试指的是测试者清楚对象内部工作机制的测试,测试自己开发的程序应该使用这种测试方式。
2⃣测试的特性:
测试的目标与其他测试活动背道而驰,测试的目的是找出错误。
测试永远不可能证明程序中彻底没有错误。
测试的结果是软件质量的一个指示器,但是测试本身并不能改善软件质量,这种妄想就像天天通过称体重来减肥一样。假如希望改进你的软件质量,仅用更多的测试是没用的,你需要的是更高质量的开发。
3⃣推荐开发者的测试方式
对每一项相关的需求进行测试,以确保需求都已经被实现;
基础测试和数据流测试;
使用一个检查表,记录你在本项目中迄今为止所犯的,以及在过去的项目中中所犯的错误类型,这有助于“猜测错误”的准确性;
推荐测试先行。
4⃣测试技巧锦囊
结构化的基础测试
需要去测试程序中的每行代码至少一次。所需基础测试最少用例数量的计算方式:
对通过子程序的直路,开始记为1;
遇到以下关键字时,加1,比如:if,and,or,while,repeat,for;
遇到每个case语句,加1,如果case语句没有默认语句,再加1.
⑤测试数据生成器
为了系统的对程序的某些部分进行测试,你可能会写一些代码。
正确设计的测试数据生成器能产生意想不到的、不寻常的测试用例;
比起手工构造测试数据,数据生成器能够更加彻底地对程序进行测试。

《代码大全》的笔记-第78页

复杂度管理
当没人知道对一处代码的改动会对其他代码带来什么影响时,系统的复杂度已经超出可控范围了。

《代码大全》的笔记-第63页

这章开始介绍各大编程语言,我忍不住想列一下
Ada:基于Pascal,适合实时与嵌入系统(PS:Ada是个我很喜欢的美女数学家)
Assembly Language:汇编,大家都知道,低级语言
C:大家都知道
C++:大家也都知道
C#:基于.NET的面向对象编程语言,很多人都知道
Cobol:这个没见过…据说流行程度仅次于VB?…据说类似英语
Fortran:本校土木系的孩子们都学这个……原因是土木的大佬们喜欢它
Java:大家都知道
JavaScript:大家会觉得很面熟因为它名字里有Java,其实关系不大,脚本语言
Perl:处理字符串的,基于C和若干UNIX程序
PHP:脚本语言,搭站的人比较熟悉
Python:解释性的交互式面向对象语言,常用写脚本和小型程序
SQL:大家都知道,PS:这个类型叫声明式语言
Visual Basic:VB,大家都知道……

《代码大全》的笔记-第26页

这一页在教育程序员们如何处理WISCA(Why Isn't Sam Coding Anything?)
其中处理方案第二条是:假装在写代码,在桌子上放一份旧的程序代码清单来瞒过老板。
…虽然从处理方式来看是个好方法…但是好欠啊!= =

《代码大全》的笔记-第68页 - 3.2 辩明你所从事的软件的类型

迭代技术往往能够减少『前期准备不足』造成的负面影响。
预先详细说明 100% 的需求和设计是不切实际的,不过对绝大多数项目来说,『尽早把哪些是最关键的需求要素和架构要素确定下来』是很有价值的。
有些项目则预先做了太多的事情,固执地坚持原有的需求和计划,后来事实证明这些需求和计划是无效的,这同样阻止了构建活动的顺利进展。
序列化开发
需求相当稳定。
设计直截了当,而且理解透彻。
开发团队对于这一应用领域非常熟悉。
项目的风险很小。
『长期可预测性』很重要。
后期改变需求、设计和编码的代价很可能较昂贵。
迭代开发(as you go)
需求并没有被理解透彻,或者处于其他理由你认为它是不稳定的。
设计很复杂,或者有挑战性,或者两者兼具。
开发团队对于这一应用领域不熟悉。
项目包含许多风险。
『长期可预测性』不重要。
后期改变需求、设计和编码的代价很可能较低。

《代码大全》的笔记-八、调试 - 八、调试


①关于调试
理解你正在编写的程序;
明确你犯了那种类型的错误;
从代码阅读者的角度分析代码质量;
审视自己解决问题的方法,花点时间来分析并改善你的调试方法,可能就是减少程序开发时间的最好方法;
审视自己修改正缺陷的方法。
②寻找缺陷步骤
把错误状态稳定下来,也就是能让缺陷稳定的重现,这几乎是最有挑战的工作之一;
确定错误的来源;
修补缺陷;
对修补的缺陷进行测试;
查找是否还有类似的错误。
③修复缺陷
在动手之前先要理解问题,知道你能真正理解问题,每次都能正确地预测结果为止;
理解程序本身,而不仅仅是问题;
验证对错误的分析;
放松一下;
保存最初的源代码,至少你能对新旧代码进行比较,看到底改了哪些地方;
治本,而不是治标;
修改代码时一定要有恰当的理由;
一次只做一个改动;
检查自己的改动;
增加暴露问题的单元测试;
搜索类似的缺陷,如果你想不出如何查找类似缺陷,这就意味着你还没有完全理解问题。
④调试工具
源代码比较工具;
编译器的警告信息,把编译器的警告级别设置为最严格;
增强的语法检查和逻辑检查;
执行性能剖测器;
测试框架;
调试器;

《代码大全》的笔记-第66页 - 4.2 编程约定

成功编程的一个关键就在于避免随意地变化。

《代码大全》的笔记-第19页

在建造帝国大厦的时候,每辆运料车运输时都留有15分钟的余地。如果某辆车没能在指定时间到位,则整个工期就会延误。
同理,对于超大型的软件项目,就需要比一般规模的项目有更高级别的规划设计。
? 支撑性测试代码(脚手架,scaffolding)、分离代码(tearing code apart)

《代码大全》的笔记-第81页 - 5.2 关键的设计概念

软件设计中的分层:
软件的分层可以为将来的重购做一些准备工作.
先更新一个较好的接口,再更新不好的代码,这样可以减小软件重构带来的风险.

《代码大全》的笔记-第2页 - 二、编写高质量代码

二、编写高质量代码
1.类
软件的首要技术使命就是管理复杂度。可以通过把整个复杂系统分解为多个子系统降低问题的复杂度。
类很像是冰山,八分之七都在水面以下,你只能看到水面以上的八分之一。
抽象数据类型(ADT)是指一些数据以及在这些数据上所能进行的操作的集合。
考虑类的一种方式,就是把它看作抽象数据类型,再加上继承和多态两个概念。
警惕有超过7个数据成员的类。
尽量使用多态,避免的大量的类型检查。
构造函数:
如果可能,应该在所有的构造函数中初始化所有的数据成员。
用私有构造函数实现单件属性。
2.子程序
子程序是为实现特定的目的而编写的一个可被调用的方法或过程。函数是有返回值的子程序;过程是没有返回值的子程序。
合理的参数个数,上线大概在7个左右。
3.防御式编程
主要思想:子程序不应传入错误数据而被破坏,哪怕是其他子程序产生的错误数据。
在代码中保留多少防御式代码?
保留那些检查重要错误的代码;
去掉检查细微错误的代码;
为技术支持人员记录错误信息;
确保留在代码中的错误信息是友好的。
4.伪代码创建子过程
用类似英语的语句描述特定的具体操作;
避免使用目标编程语言中的语法元素;
在意图的层面编写伪代码;
在一个足够低的层次上编写伪代码,以便于近乎自动地从他生成代码,然后把它编程代码中的注释。

《代码大全》的笔记-第178页

在接口中对参数的假定加以说明
在子程序内部和调用子程序的地方同时对所做的假定进行说明是值得的。不要等到把子程序写完之后再回过头去写注释——你是不会记住所有这些假定的。一种比注释还好的方法,是在代码中使用断言(assertion)。
应该对哪些接口参数的假定进行说明呢?
1. 参数是仅用于输入的、要被修改的、还是仅用于输出的;
2. 表示数量的参数的单位(英寸、英尺、米等);
3. 如果没有枚举类型的话,应说明状态代码和错误值的含义;
4. 所能接受的数值的范围;
5. 不该出现的特定数值。把程序的参数个数限制在大约7个以内
如果你发现自己一直需要传递很多参数,这就说明子程序之间的耦合太过紧密了。
如果你向很多不同的子程序传递相同的数据,就请把这些子程序组成一个类,并把哪些经常使用的数据用作类的内部数据。python中可以使用字典类型组合这些参数,并在子程序内对参数输入进行必要的验证。
anyway,还是要保持一致性为子程序传递用以维持其接口抽象的变量或对象
此处的例子:比如说你有一个对象,它通过10个访问器子程序(access routine)暴露其中的数据,被调用的子程序只需要其中的3项数据。此时究竟应该只传递子程序所需的3项特定数据,还是传递整个对象?要视子程序的接口要表达何种抽象而定。
如果你采用传递整个对象的做法,并发现自己是先创建对象,把被调用子程序所需的3项数据填入该对象,在调用过子程序后又从对象中取出3项数据的值,那就是一个证据,说明你应该只传递那3项数据而不是整个对象。(一般来说,如果在调用子程序前出现装配(set up)的代码,或者在调用子程序之后出现拆卸(take down)的代码,都是子程序设计不佳的表现。)
如果你发现自己经常需要修改子程序的参数表,而每次修改的参数都来自于同一个对象,那就说明你应该传递整个对象而不是个别数据项了。

《代码大全》的笔记-第75页 - 需求的先决条件

『需求』详细描述软件系统应该做什么,这是达成解决方案的第一步。『需求活动』,『需求开发 / requirements development』,『需求分析 / requirements analysis』,『分析 / analysis』,『需求定义 / requirements definition』,『软件需求 / software requirements』,『规格书 / specification』,『功能规格书 / functional spec』,『规格 / spec』。
明确的需求有助于确保是用户(而不是程序员)驾驭系统的功能。否则,程序员就常常会在编程期间自行决定需求。明确的需求免得你去猜测用户想要的是什么。
明确的需求还有助于避免争论。在开始编程之前,先把系统的范围(scope)确定下来。如果你和另外一个程序员对于『程序应该做什么』意见不一致,你们可以查看书面的需求,以解决分歧。
重视需求有助于减少开始编程开发之后的系统变更情况。
如果没有好的需求,你可能对问题有总体的把握,但却没有击中问题的特定方面。
需求像水。如果冻结了,就容易在上面开展建设。
对一个典型的项目来说,在编写代码之前,客户无法可靠地描述他们想要的是什么。问题并不在于客户是低级生物。就如同你做这个项目的时间越长,对这个项目的理解也就越深入一样,客户参加项目的时间越长,他们对项目的理解也越深入。开发过程能够帮助客户更好地理解自己的需求,这是需求变更的主要来源。
需求核对表。
确保每一个人都知道需求变更的代价。
建立一套变更控制程序。
使用能适应变更的开发方法。
放弃这个项目。
注意项目的商业案例。
『考虑自己的决定所带来的商业影响』的程序员的身价与黄金相当。

《代码大全》的笔记-第54页 - 软件构建:建造软件

在开发一个软件产品时,原材料甚至更加廉价,但劳动力的花销也更昂贵。变更一份报表的格式所要付出的代价,和移动房间里的一堵墙一样高昂,因为两者的主要成本构成部分都是花费人的时间。
精心计划,并非意味着事无巨细的计划或者过度的计划。你可以把房屋结构性的支撑规划清楚,而在日后再决定是用木地板还是地毯。

《代码大全》的笔记-第18页

当开发软件时,你会大量使用高级语言所提供的功能,而不会自己去编写操作系统层次的代码。你可能还要用些现成的程序库,比如说一些容器类(container classes)、科学计算函数、用户界面组件、数据库访问组件,等等。总之,自己编写那些能买得到的现成的代码通常是没有意义的。如果你要开发一款一流的软件产品,你可能会自己编写科学计算函数以便获得更快的速度和更高的精度。你还可能需要自己编写容器类、用户界面组件以及数据库访问组件等,这样做可以让产品的各个部分无缝拼接,拥有一致的外观和体验。
要权衡做一件事的原因,成本以及能带来的好处,不要盲目去做

《代码大全》的笔记-第600页 - 九、重构


1.软件演化类型
* 在修改中软件的质量要么提高,要么恶化。
* 软件演化的基本原则就是,演化应当提高程序的内在质量。
2.重构的理由
* 代码重复:DRY,Do not Repeat Yourself;
* 冗长的子程序:很少会用到长度超过一个屏幕的子程序。改善方法是提高其模块性-增加定义完善、命名准确的子程序,让他们各自集中力量做好一件事;
* 循环过长或嵌套过深;
* 内聚性太差的类;
* 拥有太多参数的参数列表;
* 变化导致多个类的相同修改;
* 同时使用的相关数据并未以类的形式组织;
* 过多使用基本数据类型;
* 某个类无所事事;
* 中间人对象无事可做;
* 子程序明明不恰当。只要看到某个子程序命名有问题,就应该立即着手修改。
* 注释被用于解释难懂的代码。不要为拙劣的代码编写文档——应当重写代码;
* 程序中的一些代码似乎是在将来的某个时候才会用到。对未来需求有所准备的代码并不是编写大量空中楼阁式的代码,而是尽可能将满足当前需求的代码清晰明白的表现出来,使未来的程序员理解这些代码到底完成了什么功能,没完成什么功能,以便根据他们的需求进行调整。
3. 数据级的重构
* 用具名常量代替神秘数值;
* 使变量的名字更加清晰且传递更多信息;
* 用函数代替表达式;
* 将基础类型转化为类。如果一个基础类型需要额外的功能或者额外的数据,那么就应该把基础类型转化为类,并添加你所需要的行为;
* 将一组类型码转换为类或者枚举类型;
4.语句级的重构
* 分解布尔表达式
* 将复杂布尔表达式转换为命名准确的布尔函数;
* 在嵌套的if-then-else语句中应该一知道答案就返回,而不是赋值给一个返回值
5.子程序级重构
* 提取子程序或者方法
* 将冗长的子程序转换为类
* 合并相似的子程序,通过参数区分他们的功能
6 类接口的重构
* 将成员函数放到另一个类中
* 将一个类变为两个类。如果一个类中的成员函数完成两种截然不同的功能,将这样的类转换为两个类
* 对于不能修改的类成员去掉其set函数。如果一个成员在对象被创建时设置,之后遍不再允许修改,那么删除其set函数,而是在构造函数中进行初始化。
7 系统级重构
* 使用工厂函数而不是简单的构造函数;
* 用异常代替错误代码,或者反其道而行之,取决于你的错误处理策略,请确保代码使用了标准的错误处理方法。
8. 安全的重构
* 保存初始代码。要保证你还能回到代码的初始状态。
* 重构的步伐请小些。这样才能理解所做修改对程序的全部影响。
* 同一时间只做一项重构。在进入到下一项重构之前,对代码进行重新编译和测试。
* 把要做的事情一步步列出来。写出一份重构列表能够让你在修改时保持思路连贯。
* 设置一个停车场。把你需要在未来进行而现在可以暂且放在一遍的修改工作记录下来。
* 利用编译器警告信息。把编译器的警告级别设置为最严格。
* 重新测试。应该把重新测试作为修改代码工作的补充。
* 检查对代码的修改。如果说第一次运行程序时检查代码是必要的,那么接下来修改代码的过程中,时刻检查代码则更为必要。另外,应该把简单的修改当作复杂的修改对待。
* 根据风险级别来调整重构方法。尤其是对于有一定风险的重构,谨慎才能避免出错。
9.重构策略
* 在增加子程序的时候重构
* 在添加类的时候重构
* 在修补缺陷的时候重构
* 关注于易出错的模块
* 关注高度复杂的模块
* 在维护环境下,改善你手中正在处理的代码。如果你正在维护某部分代码,请确保代码在离开你的时候比来之前更健康
* 开发阶段的重构是提高程序质量的最佳时机。

《代码大全》的笔记-第163页 - 类的基础:抽象数据类型

这些做法都存在一个限制,即要求调用代码直接控制数据成员,这无疑限制了 currentFont 的使用。

《代码大全》的笔记-如何使用软件隐喻 - 如何使用软件隐喻

Bachman 曾经把天文学中托勒密到哥白尼的转变,与 20 世纪初 70 年代早期计算机编程方面的变化做了比较。当 1973 年 Bachman 做这个比较时,数据处理正在从“以计算机为中心(computer-centered)”的观点向“以数据库为中心(database-centered)”的观点转变。Bachman 指出,过去的数据处理是把所有数据看作流经计算机(flowing through a computer)的连续卡片(stream of cards)(以计算机为中心的观点),现在则转变为把焦点放到数据池(pool of data)上,而计算机偶尔涉足其中(以数据库为中心的观点)。
在硬盘上创建数据和在内存中创建数据有什么区别?在 CPU 缓存中创建数据呢?假设内存掉电之后仍然能保留数据,那么硬盘存在的意义是?硬盘有没可能消失,极高速的 SSD 直接当作掉电也不会丢数据的内存来用?
除了业务逻辑,计算机本身的模型其实应该是也在偷偷着影响着我们的编程方式,只是我们似乎习以为常了,比较少注意到。不过也许只是我们这些 web 开发者吧。

《代码大全》的笔记-第3页 - 三、变量

三、变量
1.使用变量的一般事项
2.变量命名
为变量命名时要考虑的重要事项是,改名字要完全、准确地描述出该变量所代表的事物。
较长的名字适用于较少使用的变量或者全局变量;较短的名字适用于局部变量或者循环变量。
代码阅读的次数要远远大于编写的次数。要确保你所取的名字更侧重于阅读方便而不是编写方便。
3.特定数据类型命名
①.限定词命名
如果变量名中含有Total,Average,Sum,Max等限定词,请记住把限定词加在变量名的最后。
用Count和Index代替Num
关于Num限定词容易产生歧义,例如:numStudents表示学生数,studentNum表示学生序号。为了避免这种歧义,可以用Count和Index来代替,比如用studentCount表示学生总数,而用studentIndex表示学生序号。
②.为循环索引命名
如果一个变量要在循环之外使用,那么就要使用一个i,j,k之外更有意义的名字。
如果循环不是只有几行,那么读者很容易忘记i本来的含义,因此最好给循环下标取一个比i更有意义的名字。
③.为状态变量命名
为状态变量取一个比flag更好的名字。标记一般使用枚举类型、具名常量或者用作具名常量的全局变量来对其赋值。
④.为布尔变量命名
谨记典型的布尔变量名:found,error,success,ok。
给布尔变量赋予隐含“真/假”含义的名字,而且可以省略变量名开头的Is前缀。
使用肯定的变量名。
⑤.为具名常量命名
应该根据该常量所表示的含义,而不是该常量所具有的数值来命名。

《代码大全》的笔记-第61页 - 4.1 选择编程语言

Sapir-Whorf 假说,你思考的能力取决于你是否知道能够表达该思想的词汇。

《代码大全》的笔记-12.3 Floating-Point Numbers - 12.3 Floating-Point Numbers

浮点数在python中的存储格式与C中的双精度数相同。所以浮点计算可能会出现各种问题。
例如,python2.7中10个.1相加结果是0.9999999999999999,2.5,2.6中是0.99999999999999989。python2.7中5 000 000.02 - 5 000 000.01的结果为0.009999999776482582。
通常情况下可以采取换用更高精确度的变量类型,从最小值开始求和,定一个可接受的偏差值之类的方法来处理。但如果你是在处理人民币之类必须要精确结算的值:
1. 可以使用binary coded decimal(BCD,http://en.wikipedia.org/wiki/Binary-coded_decimal)来处理浮点数,python2.4增加了decimal(http://docs.python.org/library/decimal.html)模块来支持BCD算法(http://speleotrove.com/decimal/)。
2. 把浮点变量转成整型变量。
3. VB中提供了Currency这样的数据类型支持。

《代码大全》的笔记-第111页 - 5.1 设计中的挑战

设计是一个险恶的问题
希望软件设计师用一种理性的、不会犯错的方式从需求中推导出设计,这一画面根本就不现实。没有哪个系统是用这种方式设计出来的,以后也不可能有。即便是教科书和论文中的小型程序开发也是不真实的。它们都是经过修订和修饰的,知道作者让我们看到他想要做到的结果,因而是不可能在实际中发生的过程。 —— David Pamas 和 Paul Clements
『险恶的』问题就是那种只有通过解决或部分解决才能被明确的问题。你必须首先把这个问题『解决』一遍以便能够明确地定义它,然后再次解决该问题,从而形成一个可行的方案。
只有通过建造这座大桥(即解决这个问题),他们才能学会从这一问题中应该额外考虑的环节,从而才能建造出到现在依然矗立不倒的另一座桥梁。
你在学校中所开发的程序和你在职业生涯中所开发的程序的主要差异就在于,学校里的程序所解决的设计问题很少(如果有的话)是险恶的。学校里给你的编程作业都是为了你能从头到尾直线前进而设计的。
专业编程中,你刚完成设计,要求就改了。
设计是个了无章法的过程(即使它能得出清爽的结果)

《代码大全》的笔记-第13页

John Bentley说,你应该可以坐在火炉边上,品一杯白兰地或者抽一口上好的雪茄,边上坐着你心爱的猎犬,去品味一段“深奥的程序”,就像面对的是一本出色的小说那样。

《代码大全》的笔记-第25页 - Cause of Incomplete Preparation

A common cause of incomplete preparation is that the developers who are assigned to work on the upstream activities do not have the expertise to carry out their assignments. The skills needed to plan a project, create a compelling business case, develop comprehensive and accurate requirements, and create high-quality architectures are far from trivial, but most developers have not received training in how to perform these activities.

《代码大全》的笔记-第84页 - 5.2 Key Design Concepts

一个很好的比喻You might think of the lines between subsystems as being hoses with water
running through them. If you want to reach in and pull out a subsystem, that
subsystem is going to have some hoses attached to it. The more hoses you have
to disconnect and reconnect, the more wet you’re going to get. You want to
architect your system so that if you pull out a subsystem to use elsewhere you
won’t have very many hoses to reconnect and those hoses will reconnect easily.这体现了p80-81页提到的High Fan-In, Low Fan-Out设计准则。
当然,原则是一回事,怎么做是另一回事。McConnell的建议是:先收紧,再放开。

《代码大全》的笔记-第74页 - Design Challenges

Design Is a Wicked ProblemHorst Rittel and Melvin Webber defined a "wicked" problem as one that could be clearly defined only by solving it, or by solving part of it(1973).* 只有在实现之后,一些问题或者问题的严重性才被认识到。
* 所以进一步的,需要考虑怎样快速的开发原型和发现问题。(即,设计的essential difficulties虽然是wicked,但这里的考虑可以看作对accidental difficulties的解决。)
Design Is a Sloppy Process (Even If it Produces a Tidy Result)* sloppy不是tragedy。合适的版本管理工具是必须的。例如git提供的index/working dir,branch、tag和stash, blame和diff等等,正确、灵活运用,可以避免sloppy演化为tragedy。(这里还是从accidental difficulties的角度的补完)
在p87(ch5.3),McConnell从另一个角度说了一段对程序员来说非常经典的话,Software developers tend to like our answers cut and dried: “Do A, B, and C, and X, Y, Z will follow every time.” We take pride in learning arcane sets of steps that produce desired effects, and we become annoyed when instructions don’t work as advertised. This desire for deterministic behavior is highly appropriate to detailed computer programming—where that kind of strict attention to detail makes or breaks a program. But software design is a much different story.


 代码大全下载 更多精彩书评


 

农业基础科学,时尚,美术/书法,绘画,软件工程/开发项目管理,研究生/本专科,爱情/情感,动漫学堂PDF下载,。 PDF下载网 

PDF下载网 @ 2024