杂记
最后更新于:2022-04-01 05:01:44
### 编程原则
- 我的观点是:对于三种标准的结构化编程结构(顺序、选择、迭代)之外的任何控制结构的使用—也就是说,使用break, continue, return, throw–catch—都要持一种批判的态度。
—–Steven McConner
### 计算子程序中决策点的数量的技术:
- 从 1 开始, 一直往下通过程序;
- 一旦遇到以下关键字, 或者同类的词, 就加1:if, while, for, repeat, and, or
- 给case子句中的每一种情况都加1.
### 如何处理复杂度的度量结果:
- 0-5 子程序可能还不错.
- 6-10 得想办法简化子程序.
- 10+ 把子程序的某一部分拆分成另一个子程序并调用它.
### 如何对待代码复审(详查)?
- 你应该承认每一个所谓的缺陷, 然后继续详查. 承认一个批评并不意味着你认同批评的内 容.
- 在复查工作中, 你不应该试图为正在被检查的工作辩护.
- 在复查之后, 你可以独自对每一个问题进行思考, 判断它是否真的是一个缺陷.
—– Steven McConner << Code Complete 2nd>>
### 如何修正坏习惯?
不能用”没有习惯”来代替”坏习惯” – 这就是人们骤然停止吸烟,停止咒骂或者停止多食是会很难受的原因.除非有了代替方法, 如嚼口香糖. 以新习惯来取代老习惯, 要比干脆戒除老习惯容易.
```java
——-Steven McConnel
private String convertCourse(double course) {
int c = (int) course / 90;
double y = course % 90;
String yf = nft.format(y);
String[][] directions=new String[][]{new String[]{"正北","北偏东"},
new String[]{"正东","东偏南"},
new String[]{"正南","南偏西"},
new String[]{"正西","西偏北"}};
int d=(y==0.0? 0:1);
return directions[c][d]+(d==0?"":yf+"度");
}
private String convertCourse(double course) {
int c = (int) course / 90;
double y = course % 90;
String yf = nft.format(y);
if (0 == c && 0 == y) {
return "正北";
} else if (0 == c) {
return "北偏东" + yf + "度";
}
if (1 == c && 0 == y) {
return "正东";
} else if (1 == c) {
return "东偏南" + yf + "度";
}
if (2 == c && 0 == y) {
return "正南";
} else if (2 == c) {
return "南偏西" + yf + "度";
}
if (3 == c && 0 == y) {
return "正西";
} else if (3 == c) {
return "西偏北" + yf + "度";
}
return "--";
}
```
第9章: 伪代码编程过程
最后更新于:2022-04-01 05:01:42
## 第九章 The pseudocode Programming Process 伪代码编程过程
### Summary of Steps in Building Classes and Routines
### 创建一个类的步骤:
- 创建类的总体设计
- 创建类的子程序
- 复审并测试整个类
### 创建子程序的步骤:
- 设计子程序
- 检查设计
- 编写子程序的代码
- 复审并测试代码
以上过程均是迭代进行.
### 为何使用伪代码编程过程 ?
- 用类似英语的语句来精确描述特定的操作
- 避免使用目标编程语言中的语法元素.
- 在本意的层面上编写伪代码.用伪代码去描述解决问题的方法的意图,而不是去写如何在目标语言中实现这个方法.
- 在一个足够低的层次上编写伪代码,以便可以近乎自动地从它生成代码.
### 通过伪代码编程过程创建子程序
- 检查先决条件
- 定义子程序要解决的问题
- 这一子程序将要隐藏的信息
- 传给这个子程序的各项输入.
- 从该子程序得到的输出.
- 在调用程序之前确保有关的前条件成立(如输入数据的取值位于特定范围之内,有关的流已经初始化,文件已经打开或者关闭,缓冲区已经填满或清空等)
- 在子程序将控制权交回到调用程序之前,确保其后条件的成立(如输出数据位于特定范围之内,流已经初始化,文件已经打开或者关闭,缓冲区已填满或清空等).
- 为子程序命名.
- 决定如何测试子程序.
- 在标准库中搜寻可用的功能.
- 考虑错误处理.
- 考虑效率问题.
- 研究算法和数据类型.
- 编写伪代码.
- 考虑数据.
- 检查伪代码.
- 在伪代码中实验一些想法, 留下最好的想法(迭代进行).
### 编写子程序的代码 :
- 从伪代码开始
- 写出子程序的声明
- 编写第一条和最后一条语句,然后将伪代码转换为高层次的注释
- 每条注释下面填充代码
- 检查代码
- 收尾工作
- 迭代以上过程,完成
### 标准示例:
```java
====================pseudocode process========================
The routine outputs an error message based on an error code supplied by the caolling routine.The way it outputs the message depends on the current processing state, which itretrieves on its own .It returns a vlue indicating success or failure.
set the default status to “fail”
look up the message based on the error code
if the error code is valid
if doing interactive processing , display the error message interactively and declare success
if doing commnad line processing, log the error message t othe command line and declare success
if the error code isn’t valid, notify the user that an internal error has bean detected
return status information
============================End Pseudocode Process==========================
/* The routine outputs an error message based on an error code supplied by the caolling
routine.The way it outputs the message depends on the current processing state, which
*itretrieves on its own .It returns a vlue indicating success or failure.
*/
Status ReportErrorMessage(ErrorCode errorToReport){
// set the default status to “fail”
Status errorMessageStatus = Status_Failure;
// look up the message based on the error code
Message errorMessage = LookupErrorMessage( errorToReport);
// if the error code is valid
if( errorMessage.validCode()){
// determine the processing method
ProcessingMethod errorProcessingMethod = CurrentProcessingMethod();
// if doing interactive processing, display the error message
// interactively and declare success
if(errorProcessingMethod == ProcessingMethod_Interactive){
DisplayInteractiveMessage( errorMessage.getText());
errorMessageStatus = Status_Success;
}
// if doing command line processing, display the error message to command
// line and declare success
else if ( errorProcessingMethod == ProcessingMethod_CommandLine){
CommandLine messageLog;
if( messageLog.Status() == CommandLineStatus_OK){
messageLog.AddToMessageQuene();
errorMessageStatus = Status_Success;
}
else{
// cann’t do anything because the routine is already
// error processsing
}
}
else{
// cann’t do anything because the routine is already error processing
}
// if the error code isnot valid, notify the user that
// an internal error has bean detected
else {
DisplayInternactiveMessage(“Internal Error : Invalid error code in ReportErrorMessage()”);
}
// return status information
return errorMessageStatus;
}
```
// CHECKLIST: The Pseudocode Programming Process
### 核对表:伪代码编程过程
- 是否检查过已满足所有的先决条件?
- 定义好这个类要解决的问题了吗?
- 高层次的设计是否足够清晰? 能给这个类和其中的每一个子程序起一个好的名字吗?
- 考虑过该如何测试则合格类及其中每一个子程序了吗?
- 关于效率的问题,你主要从稳定的接口和可读的实现这两个角度考虑吗? 还是主要从满足资源和速度的预期目标的角度考虑过呢?
- 在标准函数库或其他代码库中寻找过可用的子程序或者组件了吗?
- 在参考书中查找过有用的算法吗?
- 是否用详细的伪代码设计好每一个子程序呢?
- 你在脑海里检查过伪代码吗? 这些伪代码容易理解吗?
- 关注过那些可能让你重返设计的警告信息了吗?(比如关于全局数据的使用,一些看上去更适合放到另一个类或子程序中的操作等)
- 是否把伪代码正确地翻译成代码?
- 你反复使用了伪代码编程过程了吗?有没有根据需要把一些子程序拆分成更小的子程序?
- 在作出假设的时候有没有对它们作出说明?
- 已经删除掉那些冗余的注释了吗?
- 你是否采用了几次迭代中的最好的那次结果?还是在第一次迭代之后就停下来?
- 你完全理解你的代码吗?这些代码容易理解吗?
### 中文要点:
- 创建类和子程序通常都是一个迭代的过程。在创建子程序的过程中获得的认识常常会反过来影响类的设计。
- 编写好的伪代码需要使用易懂的英语,要避免使用特定程序语言中才有的特性,同时要在意图的层面上写伪代码(即描述该做什么,而不是要怎么去做)。
- 伪代码编程过程是一个行之有效的做详细设计的工具,它同时让编码工作更容易。伪代码会直接转化为注释,从而确保了注释的准确度和实用性。
- 不要只停留在你所想到的第一个设计方案上。反复使用伪代码做出多种方案,然后选出其中最佳的一种方案再开始编码。
- 每一步完成后都要检查你的工作成果,还要鼓励其他人帮你来检查。这样你就会在投入精力最少的时候,用最低的成本发现错误。
### English Key Points:
- Constructing classes and constructing routines tends to be an iterative process. Insights gained while constructing specific routines tend to ripple back through the class’s design.
- Writing good pseudocode calls for using understandable English, avoiding features specific to a single programming language, and writing at the level of intent—describing what the design does rather than how it will do it.
- The Pseudocode Programming Process is a useful tool for detailed design and makes coding easy. Pseudocode translates directly into comments,ensuring that the comments are accurate and useful.
- Don’t settle for the first design you think of. Iterate through multiple approaches in pseudocode and pick the best approach before you begin writing code.
- Check your work at each step and encourage others to check it too. That way, you’ll catch mistakes at the least expensive level, when you’ve invested the least amount of effort.
第8章 :防御式编程
最后更新于:2022-04-01 05:01:40
## 第8章 :防御式编程
> checklist For Defensive Programming
### 核对表:防御式编程
------
### 一般事宜
- 子程序是否保护自己免遭有害输入数据的破坏?
- 你用断言来说明编程假定吗?其中包括了前条件和后条件吗?
- 断言是否只是用来说明从不应该发生的情况?
- 你是否在架构或高层设计中规定了一组特定的错误处理技术?
- 你是否在架构或高层设计中规定了是让错误处理更倾向于健壮性还是正确性?
- 你是否建立了隔栏来遏制错误可能造成的破坏?是否减少了其他需要关注错误处理的代码的数量?
- 代码中用到辅助调试的代码了吗?
- 如果需要启用或禁用添加的辅助助手的话,是否无需大动干戈?
- 在防御式编程时映入的代码量是否适宜–既不过多,也不过少?
- 在开发阶段是否采用了进攻式编程来使错误难以被忽视?
### 异常
- 你在项目中定义了一套标准化的异常处理方案吗?
- 是否考虑过异常之外的其他替代方案?
- 如果可能的话,是否在局部处理了错误而不是把它当成一个异常抛到外部?
- 代码中是否避免了在构造函数和析构函数中抛出异常?
- 所有的异常是否都与抛出它们的子程序处于同一抽象层次上?6).每个异常是否都包含了关于异常发生的所有背景信息?
- 代码中是否没有使用空的catch语句?(或者如果使用空的catch语句确实很合适,那么明确说明了吗?)
### 安全事宜
- 检查有害输入数据的代码是否也检查了故意的缓冲区溢出、SQL注入、HTML注入、证书溢出一级其他恶意输入数据?
- 是否检查了所有的错误返回码
- 是否捕获了所有的异常?
- 出错消息中是否避免出现有助于攻击者攻入系统所需的信息?
### 中文要点:
- 最终产品代码中对错误的处理方式要比“垃圾进,垃圾出”复杂的多。
- 防御式编程技术可以让错误更容易发现、更容易修改,并减少错误对产品代码的破坏。
- 断言可以帮助人尽早发现错误,尤其是在大型系统和高可靠性的系统中,以及快速变化的代码中。
- 关于如何处理错误输入的决策是一项关键的错误处理决策,也是一项关键的高层设计决策。
- 异常提供了一种与代码正常流程角度不同的错误处理手段。如果留心使用异常,它可以成为程序员们知识工具箱中的一项有益补充,同时也应该在异常和其他错误处理手段之间进行权衡比较。
- 针对产品代码的限制并不适用于开发中的软件。你可以利用这一优势在开发中添加有助于更快地排查错误的代码。
### English Key Points:
- Production code should handle errors in a more sophisticated way than “garbage in, garbage out.”
- Defensive-programming techniques make errors easier to find, easier to fix, and less damaging to production code.
- Assertions can help detect errors early, especially in large systems, high reliability systems, and fast-changing code bases.
- The decision about how to handle bad inputs is a key error-handling decision, and a key high-level design decision.
- Exceptions provide a means of handling errors that operates in a different dimension from the normal flow of the code.They are a valuable addition to the programmer’s toolkit when used with care, and should be weighed against other error-processing techniques.
- Constraints that apply to the production system do not necessarily apply to the development version. You can use that to your advantage, adding code to the development version that helps to flush out errors quickly.
第7章:高质量的子程序
最后更新于:2022-04-01 05:01:37
## 第7章:高质量的子程序
### 创建子程序的理由
- 降低复杂度
- 引入中间的、易懂的抽象
- 避免代码重复
- 支持子类化
- 隐藏顺序
- 隐藏指针操作
- 提高可移植性
- 简化复杂的逻辑判断
- 改善性能
### 除此之外,创建类的很多理由也是创建子程序的理由
- 隔离复杂度
- 隐藏实现细节
- 限制变化所带来的影响
- 隐藏全局数据
- 形成中央控制点
- 促成可复用的代码
- 达到特定的重构目的
### 在子程序层上设计:功能上的内聚性
- 顺序上的内聚性
- 通信上的内聚性
- 临时的内聚性
- 过程上的内聚性
- 逻辑上的内聚性
### 好的子程序的名字
- 描述子程序所做的所有事情
- 避免使用无意义的、模糊或表示不清的动词
- 不要仅通过数字来形成不同的子程序名字
- 根据需要确定子程序的名字的长度
- 给函数命名时要对返回值有所描述
- 给过程起名时要使用语气强烈的动词加宾语的形式
- 准确使用对仗词
```java
add/remove increment/decrement open/close
begin/end insert/delete show/hide
create/destory lock/unlock source/target
first/last min/max start/stop
get/put next/previous up/down
get/set old/new attack/detach
initial/done ensureCapacity dispose
current / past
new/create/construct/generate
update/refresh/alter/modify
get/find/load/obtain
open/draw/display/show/mark
process/execute/handle/dispose/deal
base on / depend on / convert to / transfer to
retrieve(判断) / indicate(表明,证明)
```
### 过程命名注意:
- 一个具有功能内聚性的过程通常是指对一个对象执行一种操作.
- 过程的名字应该能够反映出该过程所做的事.
- 你不用再过程中加入对象的名字(宾语).
- 常用的操作确立命名规则
### 7.5 如何使用子程序的参数
- 按照输入-修改-输出的顺序排列参数
- 考虑自己创建in和out关键字
- 如果几个子程序都用了类似的一些参数,应该让这些参数的排列顺序保持一致:因为子程序的参数顺序可以产生记忆效应.
- 使用所有的参数
- 把状态或出错变量放到最后
- 不要把子程序的参数用到工作变量
- 在接口中队参数的假定加以说明
- 把子程序的参数个数限制在大约7个以内
- 考虑对参数采用某种表示输入、修改、输出的命名规则
- 为子程序传递用以维持其接口抽象的变量或对象
- 使用具名参数
- 确保实际参数与形式参数相匹配
> checklist For High-Quality Routines
### 核对表:高质量的子程序
-------
### 大局事项
- 创建的子程序的理由充分吗?
- 一个子程序中所有适于单独提出的部分是不是已经被提出到单独的子程序中了?
- 过程的名字是否用了强烈、清晰的“动词+宾语”词组?函数的名字是否描述了其返回值?
- 子程序的名字是否描述了它所做的全部事情?
- 是否给常用的操作建立了命名规则?
- 子程序是否具有强烈的功能上的内聚性?即它是否做且只做一件事,并且把它做好?
- 子程序之间是否有较松的耦合?子程序与其他子程序之间的连接是否是小的,明确的,可见的和灵活的?
- 子程序的长度是否是有其功能和逻辑自然确定,而非遵循任何人为的编码标准?
### 参数传递事宜
- 整体来看,子程序的参数是否表现出一种具有整体性且一致的接口抽象?
- 子程序参数的排列顺序是否合理?是否与类似的子程序的参数排列顺序相符?
- 接口假定是否已在文档中说明?
- 子程序的参数个数是否没有超过7个?
- 是否用到了每一个输入参数?
- 是否用到了每一个输入参数?
- 子程序是否避免了吧输入参数用做工作变量?
- 如果子程序是一个函数,那么它是否在所有可能的情况下都能够返回一个合法的值?
### 中文要点:
- 创建子程序最主要的目的是提高程序的可管理性,当然也有其他一些好的理由。其中,节省代空间只是一个次要原因:提高可读性、可靠性和可修改性等原因都更重要一些。
- 有时候,把一些简单的操作写成独立的子程序也非常有价值。
- 子程序可以按照其内聚性分为很多类,而你应该让大多数程序具有功能上的内聚性,这是最佳的一种内聚性。
- 子程序的名字是它的质量的指示器。如果名字糟糕但恰如其分,那就说明这个子程序设计得很差劲。如果名字糟糕而且又不准确,那么它就反映不出程序是干什么的。不管怎样,糟糕的名字都意味着程序需要修改。
- 只有在某个子程序的主要目的是返回由其名字所描述的特定结果时,才应该使用函数。
- 细心的程序员会非常谨慎地使用宏,而且只在万不得已时才用。
### English Key Points:
- The most important reason to create a routine is to improve the intellectual manageability of a program, and you can create a routine for many other good reasons. Saving space is a minor reason;
- improved readability, reliability, and modifiability are better reasons.
- Sometimes the operation that most benefits from being put into a routine of its own is a simple one.
- The name of a routine is an indication of its quality. If the name is bad and it’s accurate, the routine might be poorly designed. If the name is bad and it’s inaccurate, it’s not telling you what the program does. Either way, a bad name means that the program needs to be changed.
- Functions should be used only when the primary purpose of the function is to return the specific value described by the function’s name.
- Careful programmers use macro routines and inline routines with care, and only as a last resort.
第6章 可以工作的类
最后更新于:2022-04-01 05:01:35
## 第6章 可以工作的类
> // checklist For Design in Construction
> 核对表:软件构造中的设计
### 设计实践
- 你已经做过多次迭代,并且从众多尝试结果中选择最佳的一种,而不是简单选择第一次尝试的结果吗?
- 你尝试用多种方案来分解系统,以确定最佳的方案吗?
- 你同时用自上而下和自下而上的方法来解决设计问题吗?
- 为了解决某些特定的问题,你对系统中的风险部分或者不熟悉的部分创建过原型、写出数量最少的可抛弃的代码吗?
- 你的设计方案被其他人检查了吗(无论正式与否)?
- 你一直在展开设计,知道实施细节跃然纸上吗?
- 你用某种适当的技术—比如说Wiki、电子邮件、挂图、数码相机、UML、CRC卡或者在大马鞋注释–来保留设计成果吗?
### 设计目标
- 你的设计是否充分地处理了由系统架构层定义出并且推迟确定的事项吗?
- 你的设计被划分为层次吗?
- 你对把这一程序分解成为子程序、包和类的方式感到满意吗?
- 你把对这个类分解成为子程序的方法感到满意吗?
- 类与类之间的交互关系是否已设计成最小化了?
- 类和子程序是否被设计为能够在其他的系统中重用?
- 程序是不是易于维护?
- 设计是否精简?设计出来的每一部分都是绝对必要的吗?
- 设计中是否采用了标准的技术?是否避免使用怪异且难以理解的元素?
- 整体而言,你的设计是否有助于最小化偶然性和本质性的复杂度吗?
> 6.1 类的基础:抽象数据类型(ADTs)
### 使用ADT的益处
- 可以隐藏实现细节
- 改动不会影响到整个程序
- 让接口能提供更多的信息
- 更容易提高性能
- 让程序的正确性更加显而易见
- 程序更具有自我说明性
- 无须在程序内到处传递数据
- 你可以像在现实世界中那样操作尸体,而不用再底层实现上操作它
> 良好的类接口
### 好的抽象
- 类的接口应该展现一致的抽象层次?
- 一定要理解所实现的抽象是什么?
- 提供成对的服务?
- 把不相关的信息转移到其他类中?
- 尽可能让接口可编程,而不是表达语义?
- 谨防在修改时破坏接口的抽象?
- 不要添加于接口抽象不一致的公用成员?
- 同时考虑抽象性和内聚性?
> Good Encapsulation
### 良好的封装
- 尽可能地限制类和成员的可访问性?
- 不要公开暴露成员数据?
- 避免吧私用的实现细节放入类的接口中?
- 不要对类的使用者作出任何假设?
- 避免使用友元类?
- 不要因为一个子程序里仅使用公用子程序,就把它归入公开接口?
- 要格外警惕从语义上破坏封装性?
- 留意过于紧密的耦合关系?
> Containment (“has a” Relationships)
### - 包含(“有一个…”的关系)
- 通过包含来实现“有一个/has a ” 的关系?
- 在万不得已时通过private 继承来实现“有一个”的关系?
- 警惕有超过7个数据成员的类?
> Inheritance (“is a ” Relationship)
### 继承(“是一个…”关系)
- 用public 继承来实现”是一个“的关系?
- 要么使用继承并进行详细说明,要么就不要使用它?
- 遵循Liskov替换原则?
- 确保只继承需要继承的部分?
- 不要”覆盖“一个不可覆盖的成员函数?
- 把共用的接口、数据及操作放到继承树种尽可能高的位置?
- 只有一个实例的类是值得怀疑的?
- 只有一个派生类的基类也是值得怀疑的?
- 派生后覆盖了某个子程序,但在其中没有做任何操作,这种情况也是值得怀疑的?
- 避免让继承体系过深?
- 尽量使用多态,避免大量的类型检查?
- 让所有数据都是private ,而非protected
> Why Are There So Many Rules for Inheritance
### 为什么有这么多关于继承的规则?
- 如果多个类共享数据而非行为,应该创建这些类可以包含的共用对象?
- 如果多个类共享行为而非数据,应该让它们从共同的基类继承而来,并在基类里定义共用的子程序?
- 如果多个类即共享数据也共享行为,应该让它们从一个共同的基类继承而来,并在基类里定义共用的数据和子程序?
- 当你想由基类控制接口时,使用继承;当你想自己控制接口时,使用包含?
> Reasons to Create a Class
### – 创建类的原因
- 为现实世界中的对象?
- 为抽象的对象建模?
- 降低复杂度?
- 隔离复杂度?
- 隐藏实现细节?
- 限制变动的影响范围?
- 隐藏全部数据?
- 让参数传递更顺畅?
- 建立中心控制点?
- 让代码更加容易复用?
- 为程序族做计划?
- 把相关操作包装到一起?
- 实现某种特定的重构?
> checklist For Class Quality
### 核对表:类的质量
### 抽象数据类型
1. 你是否把程序中的类都砍做事抽象数据类型了?是否从这个角度评估它们的接口?
### 抽象
- 类是否有一个中心目的?
- 类的命名是否恰当?其名字是否表达了其中心目的?
- 类的接口是否展现一致的抽象?
- 类的接口是否足够抽象,使你能不必顾虑它是如何实现其服务的?你能把类看做黑盒子吗?
- 类提供的服务是否足够完整,能让其他类无须动用其内部数据?
- 是否已从类中除去无关信息?
- 是否考虑过把类进一步分解为组件类?是否已尽可能将其分解?
- 在修改类是是否维持了其接口的完整性?
### 封装
- 是否把类的成员的可访问性降到最小?
- 是否避免暴露类中的数据成员?
- 在编程语言所许可的范围内,类是否尽可能的对其他的类隐藏自己的实现细节?
- 类是否避免对其使用者,包括其派生类会如何使用它做了假设?
- 类是否不依赖于其他类?它是松散耦合的吗?
### 继承
- 继承是否只用来建立”是一个“的关系?也就是说,派生类是否遵循了LSP原则?
- 类的文档中是否记述了其继承策略?
- 派生类是否避免了”覆盖“不可覆盖的方法?
- 是否把公用的接口、数据和行为都放到尽可能高的继承层次中了?
- 继承层次是否很浅?
- 基类中所有的数据成员是否都被定义为private而非protected的了?
### 跟实现相关的其他问题
- 类中是否只有大约7个或者更少的数据成员?
- 是否把类直接或间接调用其他类的子程序的数量减到最少了?
- 类是否只在绝对必要时才与其他的类相互协作?
- 是否在构造函数中初始化了所有的数据成员?
- 除非拥有经过测量的、创建浅层副本的理由,类是否都被设计为当做深层副本使用?
### 与语言相关的问题
- 你是否研究过所用编程语言里和类相关的各种特定问题?
### 中文要点:
- 类的接口应提供一致的抽象。很多问题都是由于违背该原则而引起的。
- 类的接口应隐藏一些信息――如某个系统接口、某项设计决策、或一些实现细节。
- 包含往往比继承更为可取――除非你要对“是一个/is a”的有关系建模。
- 继承是一种有用的工具,但它却会增加复杂度,这有违于软件的首要技术使命――管理复杂度。
- 类是管理复杂度的首选工具。要在设计类时给予足够的关注,才能实现这一目标。
### English Key Points:
- Class interfaces should provide a consistent abstraction. Many problems arise from violating this single principle.
- A class interface should hide something—a system interface, a design decision, or an implementation detail.
- Containment is usually preferable to inheritance unless you’re modeling an “is a” relationship.
- Inheritance is a useful tool, but it adds complexity, which is counter to the Primary Technical Imperative of minimizing complexity.
- Classes are your primary tool for managing complexity. Give their design as much attention as needed to accomplish that objective.
第5章 软件构建中的设计
最后更新于:2022-04-01 05:01:33
## 第5章 软件构建中的设计
**关于设计启发的总结**
### 下面是对主要的设计中的启发式方法的总结:
- 寻找现实世界的对象
- 辨别对象及其属性(数据和方法)
- 确定可以对各个对象进行的操作
- 确定各个对象能对其他对象进行的操作
- 确定对象的那些部分是可见的.
- 形成一致的抽象
- 封装实现细节
- 在可能的情况下继承
- 藏住秘密(信息隐藏)
两种秘密:
- 隐藏复杂度: 这样就不用去应付它, 除非你要特别关注它时.
- 隐藏变化源: 当变化发生时, 器影响能限定在局部范围内.
信息隐藏的障碍:
- 信息过度分散?
- 循环依赖?
- 把类内数据误认为全局数据?
- 可以觉察的性能损耗?
信息隐藏的价值:
- 具有独特的启发力?
- 它能够激发出有效的设计方案?
- 找出容易变化的区域
- 找出看起来容易变化的项目?
- 把容易变化的项目分离出来?
- 把看起来容易变化的项目隔离开来?
- 容易变化的区域:
- 业务规则
- 对硬件的依赖性
- 输入和输出
- 非标准的语言特性
- 困难的设计区域和构建区域
- 状态变量
- 保持松散耦合
耦合的标准:规模,可见性,灵活性
### 耦合的种类:
- 简单数据参数耦合
- 对象参数耦合
- 简单对象耦合
- 语义上的耦合
### 语义耦合示例:
- Module1向Module2传递一个控制标志, 告诉Module2该做什么.这种方法要求Module1 对 Module2的内部工作细节有所了解, 也就是说需要了解Module2 对控制标志的使用, 如果 Module2 把这个标志定义成一种特定的数据类型(枚举或对象),还过得去;
- Module2 在 Module1修改了某个全局数据之后使用该全局数据, 这种方式就要求Module2 假设Module1对该数据作出的修改符合Module2的需要.并且Module1已经在恰当地的时间调用过.
- Module1的接口要求它的Module1.initialize()子程序必须在Module1.runtime()方法之前调用. Module2知道Module1.runtime() 无论如何都会调用Module1.initialize(),所以, Module2 在实例化Module1之后并未去调用 initialize()子程序, 因为它知道调用rountime()时会调用它;
- Module1把 Object传给Module2, 由于Module1知道Module2值用了Object的7个方法中的3个, 因此它只部分初始化Object–只包含那3个方法所需的数据;
- Module1 把BaseObject 传给Module2, 由于Module2 知道Module1实际上只传给它是BeviedObject, 所以它把BaseObject 强制转换成DerivedObject, 并且调用DerivedObject 特有的方法.
记住 : 类和子程序是用于降低复杂度的首选和最重要的工具.
查阅常用的设计模式:
- 设计模式通过吧对话提升到一个更高层次来简化交流
- 设计模式通过提升多种设计方案二带来启发性价值
- 设计模式通过吧常见解决方案的细节予以制度化以减少出错.
- 探寻通用的设计模式
### 下列的启发式方法优势也很有用:
- 高内聚性
- 构造分层结构
- 严格描述类契约
- 分配职责
- 为测试而设计
- 避免失误
- 有意识地选择绑定时间
- 创建中央控制点
- 画一个图,顶一千句话
- 保持设计模块化
### 设计中的挑战
- 设计是一个险恶的问题.
- 设计是一个无章法的过程.即使他能看出清爽的成果.
- 设计就是确定取舍和调整顺序的过程.
- 设计受到诸多限制.
- 设计是不确定的.
- 设计是一个启发式的过程.
- 设计是自然而然形成的.
### 关键的设计概念
- 如今的首要技术使命:管理复杂度
- 偶然的难题和本质的难题
- 管理复杂度的重要性
- 如何应对复杂度
### 理想的设计特征
- 最小的复杂度
- 易于维护
- 松散耦合
- 可扩展性
- 可重用性
- 高扇入
- 低扇出
- 精简性
- 层次性
- 标准技术
### 设计的层次
- 软件系统(1层)
- 分解成为子系统
- 常用的子系统
- 业务规则
- 用户界面
- 数据库的访问
- 对系统的依赖性
- 分解成类(或者包,3层)
- 分解为子程序(第4层)
- 子程序内部设计(第5层)
### 中文要点
- 软件的首要技术使命就是管理复杂度。以简单性作为努力目标的设计方案对此最有帮助。
- 简单性可以通过两种方式来获取:一是减少在同一时间所关注的本质性复杂度的量,二是避免生成不必要的偶然的复杂度。
- 设计是一种启发式的过程。固执于某一种单一方法会损害创新能力,从而损害你的程序。
- 好的设计都是迭代的。你尝试设计的可能性越多,你的最终设计方案就会变得越好。
- 信息隐藏是个非常有价值的概念。通过询问“我应该隐藏些什么?”能够解决很多困难的设计问题。
- 很多有用有趣的、关于设计的信息存在于本书之外。这里所给出的观点只是对这些有价值资源的一点提示而已。
### English Key Points:
- Software’s Primary Technical Imperative is managing complexity. This is accomplished primarily through a design focus on simplicity.
- Simplicity is achieved in two general ways: minimizing the amount of essential complexity that anyone’s brain has to deal with at any one time and keeping accidental complexity from proliferating needlessly.
- Design is heuristic. Dogmatic adherence to any single methodology hurts creativity and hurts your programs.
- Good design is iterative; the more design possibilities you try, the better your final design will be.
- Information hiding is a particularly valuable concept. Asking, “What should I hide?” settles many difficult design issue.
- Lots of useful, interesting information on design is available outside this book. The perspectives presented here are just the tip of the iceberg.
第4章 关键的构建决策
最后更新于:2022-04-01 05:01:31
## 第4章 关键的构建决策
> // checklist For Major Construction Practices
> 核对表:主要的构建实践
### 编码
- 你有没有确定,多少设计工作将预先进行,多少工作在键盘上进行(在编写代码的同时)?
- 你有没有规定诸如名称、注释、代码格式等”编码约定“?
- 你有没有规定特定的由软件架构确定的编码实践,比如如何处理错误条件、如何处理安全性事项、对于类接口有哪些约定、可重用的代码遵循哪些标准、在编码时考虑多少性能因素等?
- 你有没有找到自己在技术浪潮中的位置,并相应调整自己的措施?如果必要,你是否知道如何”深入一种语言去编程“,而不受限于语言(仅仅”在一种语言上编程”)?
### 团队工作
- 你有没有定义一套集成工序–即,你有没有定义一套特定的步骤,规定程序员在把代码checkin(签入)到主源码(代码库)中之前,必须履行这些步骤?
- 程序员是结对编程,还是独立编程,或者这二者的某种组合?
### 质量保证
- 程序员在编写代码之前,是否先为之写测试用例?
- 程序员会为自己的代码写单元测试吗(无论先写还是后写)?
- 程序员在check in 代码之前,会用调试器单步跟踪整个代码流程吗?
- 程序员在checkin代码之前,是否进行集成测试(integration-test)?
- 程序员会复审或检查别人的代码吗?
### 工具
- 你是否选用了某种版本控制工具?
- 你是选定了一种语言,以及语言的版本或编译器版本?
- 你是否选择了某个编程框架,或者明确的决定不使用编程框架?
- 你是决定允许使用非标准的语言特性?
- 你是否选定并拥有了其他将要用到的工具—编辑器、重构工具、调试器、测试框架、语法检查器等?
### 中文要点:
- 每种编程语言都有其优点和弱点。要知道你使用的语言的明确优点和弱点。
- 在开始编程之前,做好一些约定(convention)。“改变代码使之符合这些约定”是近乎不可能的。
- “构建的实践方法”的种类比任何单个项目能用到的要多。有意识地选择最适合你的项目的实践方法。
- 问问你自己,你采用的编程实践是对你所用的编程语言的正确响应,还是受它的控制?请记得“深入一种语言去编程”,不要仅“在一种语言上编程”。
- 你在技术浪潮中的位置决定了哪种方法是有效的——甚至是可能用到的。确定你在技术浪潮中的位置,并相应调整计划和预期目标。
### English Key Points:
- Every programming language has strengths and weaknesses. Be aware of the specific strengths and weaknesses of the language you’re using.
- Establish programming conventions before you begin programming. It’s nearly impossible to change code to match them later.
- More construction practices exist than you can use on any single project.Consciously choose the practices that are best suited to your project.
- Your position on the technology wave determines what approaches will be effective—or even possible.Identify where you are on the technology wave, and adjust your plans and expectations accordingly.
第3章 三四而后行:前期准备
最后更新于:2022-04-01 05:01:28
## 第三章 三四而后行:前期准备
> // checklist For Architecture
> 核对表:架构
### 针对各架构主题
- 程序的整体组织结构是否清晰?是否包含一个良好的架构全局观(及其理由)?
- 是否明确定义了主要的构造块(包括每个构造块的职责范围及与其他构造块的接口)?
- 是否明显涵盖了“需求”中列出的所有功能(每个功能对应的构造块不太多也不太少)?
- 是否描述并论证了那些最关键的类?
- 是否描述并论证了数据设计?
- 是否详细定义了数据库的组织结构和内容?
- 是否指出了所用关键的业务规则,并描述其对系统的影响?
- 是否描述了用户界面设计的策略?
- 是否将用户界面模块化,使界面的变更不会影响程序其余部分?
- 否估算了稀缺资源(如线程、数据库连接、句柄、网络带宽等)的使用量,是否描述并论证了资源管理的策略?
- 是否描述了架构的安全需求?
- 架构是否为每一个类、每一个子系统、或每一个功能域(functionality area)提出空间和时间预算?
- 架构是否描述了如何达到可伸缩性?
- 架构是否关注互操作性?
- 是否描述了国际化/本地化的策略?
- 是否提供了一套内聚的错误处理策略?
- 是否规定了容错的办法(如果有需要)?
- 是否证实了系统各个部分的技术可行性?
- 是否详细描述了过度工程(overengineering)的方法?
- 是否包含了必要的“买 vs. 造”的策略?
- 架构是否描述了如何加工被复用的代码,使之符合其他架构目标?
- 是否将架构设计得能够适应很可能出现的变更?
### 架构的总体质量
- 架构是否解决了全部需求?
- 有没有那个部分是“过度架构”或“欠架构(underarchitected)”?是否明确宣布了在这方面的预期指标?
- 整个架构是否在概念上协调一致?
- 顶层设计是否独立于实现它的机器和语言?
- 是否说明了所有主要的决策的动机?
- 你,作为一名实现该系统的程序员,是否对这个系统的架构感觉良好?
### 中文要点:
- 构建活动的准备工作的根本目标在于降低风险。要确认你的准备活动是在降低风险,而非增加风险.
- 如果你想开发高质量的软件,软件开发过程必须由始至终关注质量。在项目初期关注质量,对产品质量的正面影响比在项目末期关注质量的影响要大。
- 程序员的一部分工作是教育老板和合作者,告诉他们软件开发过程,包括在开始编程之前进行充分准备的重要性。
- 你所从事的软件项目的类型对构建活动的前期准备有重大影响——许多项目应该是高度迭代式的,某些应该是序列式的。
- 如果没有明确的问题定义,那么你可能会在构建期间解决错误的问题。
- 如果没有做完良好的需求分析工作,你可能没能察觉待解决的问题的重要细节。如果需求变更发生在构建之后的阶段,其代价是“在项目早期更改需求”的 20至 100 倍。因此在开始编程之前,你要确认“需求”已经到位了。
- 如果没有做完良好的架构设计,你可能会在构建期间用错误的方法解决正确的问题。架构变更的代价随着“为错误的架构编写的代码数量”增加而增加,因此,也要确认“架构”已经到位了。
- 理解项目的前期准备所采用的方法,并相应地选择构建方法。
### English Key Points:
- The overarching goal of preparing for construction is risk reduction. Be sure your preparation activities are reducing risks, not increasing them.
- If you want to develop high-quality software, attention to quality must be part of the software-development process from the beginning to the end. At tention to quality at the beginning has a greater influence on product quality than attention at the end.
- Part of a programmer’s job is to educate bosses and coworkers about the software-development process,including the importance of adequate preparation before programming begins.
- The kind of project you’re working significantly affects construction prerequisites—many projects should be highly iterative, and some should be more sequential.
- If a good problem definition hasn’t been specified, you might be solving the wrong problem during construction.
- If a good requirements work hasn’t been done, you might have missed important details of the problem.Requirements changes cost 20 to 100 times as much in the stages following construction as they do earlier, so be sure the requirements are right before you start programming.
- If a good architectural design hasn’t been done, you might be solving the right problem the wrong way during construction. The cost of architectural changes increases as more code is written for the wrong architecture, so be sure the architecture is right too.
- Understand what approach has been taken to the construction prerequisites on your project and choose your construction approach accordingly.
第2章 用隐喻来更充分的理解软件开发
最后更新于:2022-04-01 05:01:26
## 第2章 用隐喻来更充分的理解软件开发
### 中文要点:
- 隐喻是启示而不是算法.因此它们往往有一点随意(sloopy).
- 隐喻把软件开发过程与其他你熟悉的活动联系在一起,帮助你更好的理解.
- 有些隐喻比其他一些隐喻更贴切.
- 通过把软件的构建过程比作是房屋的建设过程,我们可以发现,仔细的准备是必要的,而大型项目和小型项目之间也是有差别的。
- 通过把软件开发中的实践比作是智慧工具箱中的工具,我们又发现,每位程序员都有许多工具,但并不存在任何一个能适用于所有工作的工具,因地制宜的选择正确工具是成为能有效编程的程序员的关键。不同的隐喻彼此并不排斥,应当使用对你最有益处的某种隐喻组合。
### English Key Points:
- Metaphors are heuristics, not algorithms. As such, they tend to be a little sloppy.
- Metaphors help you understand the software-development process by relating it to other activities you already know about.
- Some metaphors are better than others.
- Treating software construction as similar to building construction suggests that careful preparation is needed and illuminates the difference between large and small projects.
E Thinking of software-development practices as tools in an intellectual toolbox suggests further that every programmer has many tools and that no single tool is right for every job. Choosing the right tool for each problem is one key to being an effective programmer.
第1章 欢迎进入软件构建的世界
最后更新于:2022-04-01 05:01:24
## 第1章 欢迎进入软件构建的世界
> checklist for Requirements
### –核对表:需求
这张需求核对表包含了一系列的问题–问问自己项目的需求工作做得如何。本书并不会告诉你如何做出好的需求分析,所以列表里面也不会有这样的问题。在开始构建之前,用这份列表做一次“心智健全”检查,看看你的地基到底有多坚固–用“需求里氏地震”来衡量。
并不是核对表中所有的问题都适用于你的项目。如果你做的是一个非正式的项目,那么你会发现有些东西根本就不需要考虑。你还会发现一些问题你需要考虑,但并不需要做出正式的回答,如果你是在做一个大型的、正式的项目,你也许就要逐条考虑了。
### 针对功能需求
- 是否详细定义了系统的全部输入,包括其来源、精度、取值范围、出现的频率等?
- 是否详细定义了系统的全部输出,包括目的地、精度、取值范围、出现频率、格式等?
- 是否详细定义了所有输出格式(WEB页面、报表等)?
- 是否详细定义了全部外部通信接口,包括握手协议、纠错协议、通信协议等?
- 是否列出了用户想要做的全部事情?
- 是否详细定义了每个任务所用的数据,以及每个任务得到的数据?
### 针对非功能需求(质量需求)
- 是否为全部必要的操作,从用户的视角,详细描述了期望响应时间?
- 是否详细描述了其他与计时有关的考虑,例如处理时间、数据传输率、系统吞吐量?
- 是否详细定义了安全级别?
- 否详细定义了可靠性,包括软件失灵的后果、发生故障时需要保护的至关重要的信息、错误检测与恢复的策略等?
- 是否详细定义了机器内存和剩余磁盘空间的最小值?
- 是否详细定义了系统的可维护性,包括适应特定功能的变更、操作系统的变更、与其他软件的接口的变更能力?
- 是否包含对“成功”的定义?“失败”的定义呢?
### 需求的质量
- 需求是用用户的语言书写的吗?用户也这么认为吗?
- 每条需求都不与其他需求冲突吗?
- 是否详细定义了相互竞争的特性之间的权衡—例如,健壮性与正确性之间的权衡?
- 是否避免在需求中规定设计(方案)?
- 需求是否在详细程度上保持相当一致的水平?有些需求应该更详细地描述吗?有些需求应该更粗略地描述吗?
- 需求是否足够清晰,即使转交给一个独立的小组去构建,他们也能理解吗?开发者也这么想吗?
- 每个条款都与待解决的问题及其解决方案相关吗?能从每个条款上溯到它在问题域中对应的根源吗?
- 是否每条需求都市可测试的?是否可能进行独立的测试,以检测满不满足各项需求?
- 是否详细描述了所有可能的对需求的改动,包括各项改动的可能性吗?
### 需求的完备性
- 对于在开始开发之前无法获知的信息,是否详细描述了信息不完全的区域?
- 需求的完备度能否达到这种程度:如果产品满足所有的需求,那么它就是可接受的?
- 你对全部需求都感到舒服吗?你是否已经去掉了那些不可能实现的需求—那些只是为了安抚客户和老板的东西?
### 中文要点:
- 软件构建是软件开发的核心活动;构建活动是每个项目中唯一一项必不可少的工作.
- 软件构建的主要活动包括:详细设计,编码,调试,集成,开发者测试(包括单元测试和集成测试).
- 构建也常被称作”编码”和”编程”.
- 构建活动的质量对软件的质量有着实质性的影响.
- 最后,你对”如何进行构建”的理解程度,决定了你这名程序员的优秀程度.
### English Key Points:
- Software construction the central activity in software development;construction is the only activity that’s guaranteed to happen on every project.
- The main activities in construction are detailed design, coding, debugging, and developer testing.
- Other common terms for construction are “coding and debugging” and “programming.”
- The quality of the construction substantially affects the quality of the software.
- In the final analysis, your understanding of how to do construction determines how good a programmer you are, and that’s the subject of the rest of the book.