Big Ben

一个半吊子的编码爱好者

0%

Edit

Promise的意思是约定,相对应的词是Defer,是推迟。他的用意是在异步的JavaScript世界引入同步写法。看看下面的“邪恶金字塔”。

var fs = require('fs');
fs.readFile('sample01.txt', 'utf8', function (err, data) {
fs.readFile('sample02.txt', 'utf8', function (err,data) {
fs.readFile('sample03.txt', 'utf8', function (err, data) {
fs.readFile('sample04.txt', 'utf8', function (err, data) {

});
});
});
});

既难看又难用。有了Promise以后,这种嵌套的金字塔代码就变成平级的链式代码。而且可以保证调用的顺序。

JavaScript官方在2013年正式宣布支持Promise语法,尽管在很多浏览器端,仍然有不兼容的情况出现。不过可见这是大势所趋。在官方宣布支持之前,已经有很多第三方库做了polyfill,就是对原有函数进行了包装,以使其支持Promise。例如下面的库:

  • Q
  • when
  • WinJS
  • RSVP.js

上面这些库和 JavaScript 原生 Promise 都遵守一个通用的、标准化的规范:Promises/A+。jQuery的Promise-Defer的实现不符合这个标准,使用需谨慎。

另外各种Promise的实现虽概念一致,但仍不尽相同。看了很多文章,仍然云里雾里。刚开始接触的是这一篇文章:

Promise的术语

  • 肯定(fulfilled)
    • 该 Promise 对应的操作成功了
  • 否定(rejected)
    • 该 Promise 对应的操作失败了
  • 等待(pending)
    • 还没有得到肯定或者否定结果,进行中
  • 结束(settled)
    • 已经肯定或者否定了
  • 类 Promise (thenable)
    • 一个对象是否是“类 Promise”(拥有名为“then”的方法)的。

初始状态是pending,settled=fulfilled or rejected,状态转换只有一次,而且不能逆转。

var promise = new Promise(function(resolve, reject) {
// 做一些异步操作的事情,然后……

if (/* 一切正常 */) {
resolve("Stuff worked!");
}
else {
reject(Error("It broke"));
}
})

promise.then(function(result) {
console.log(result); // “完美!”
}, function(err) {
console.log(err); // Error: "出问题了"
});;

构造函数

构造器接受一个函数作为参数,它会传递给这个回调函数两个变量 resolve 和 reject。在回调函数中做一些异步操作,成功之后调用 resolve,否则调用 reject。

上面这段话的意思是,

  • 首先,Promise对象用一个回调函数来构造。
  • 其次,看下面这段code
    • 这个回调函数有两个参数,这两个参数由Promise来负责传递(resolve& reject)。
    • 回调函数中定义一些自己需要做的异步操作(asyncOp)。
    • 异步操作结束时,自行判断成功失败,失败调用reject函数,成功调用resolve函数。
    • Promise使用时至少与一个then配对,在then里面做asyncOp之后想做的事情。Promise里面的结果通过resolve和reject传递的参数(err&res)传到then里。
function polyFill(){
return new Promise(function (resolve, reject){
fs.asyncOp(function (err, res){
if (err)
reject(err);
else
resolve(res);
});
});
}
polyFill().then(resolveFunc, rejectFunc);

resolve & reject

这两个是Promise类的成员函数,使用的时候,也可以像下面这样调用:

promise.resolve(val).then(...);
promise.reject(Error(err)).then(...);

这样的调用和下面的常规调用是等效的:

var promise = Promise(function(resolve, reject){
if (good){
resolve(val);
}
else {
reject(Error(err));
}
});
promise.then(...);

各路贴子和解说中都鲜少提及resolve和reject函数的参数是什么,有什么用。我之前引用的两个链接都有说到,但我感觉讲的都不是很清楚。下面理一下我的思路:

resolve

  • resolve总是返回一个Promise对象
  • 接收到promise对象参数的时候, 返回的还是接收到的promise对象,而且下一个then会等到这个Promise定义的异步操作结束时才会运行。
  • 接收到thenable类型的对象的时候, 返回一个新的promise对象,这个对象具有一个 then 方法
  • 接收的参数为其他类型的时候(包括JavaScript对或null等), 返回一个将该对象作为值的新promise对象,这个值会被当作参数传到下一个定义了resolve函数参数的then里面。
    reject
    • reject与resolve类似,返回的也是一个Promise对象
    • reject通常传入一个Error()对象。这不是必须的,但Error对象包含了调用栈,方便调试。

错误处理

上面提到了reject已经涉及到一部分错误处理了。在Promise里面,我们还可以用catch。

get('story.json').then(function(response) { 
console.log("Success!", response);
}).catch(function(error) {
console.log("Failed!", error);
});

JavaScript Promises这里提到:

这里的 catch 并无任何特殊之处,只是 then(undefined, func) 的语法糖衣,更直观一点而已。catch相当于:

get('story.json').then(function(response) {
console.log("Success!", response);
}).then(undefined, function(error) {
console.log("Failed!", error);
});

总结一下:
在Promise中的任何错误,包括:

  • reject调用
  • Promise中的抛出的异常

会落到Promise后面第一个定义了reject函数参数的then分支里。

创建序列

JavaScript Promises中,详细解释了如何操作一组Promise,从真正的用一组Promise到使用Promise.all。这里讲的很好了,我直接拷过来。

  1. 最直接的数组forEach
// 从一个完成状态的 Promise 开始
var sequence = Promise.resolve();

// 遍历所有章节的 url
story.chapterUrls.forEach(function(chapterUrl) {
// 从 sequence 开始把操作接龙起来
sequence = sequence.then(function() {
return getJSON(chapterUrl);
}).then(function(chapter) {
addHtmlToPage(chapter.html);
});
});
  1. array.reduce 精简一下上面的代码:
// 遍历所有章节的 url
story.chapterUrls.reduce(function(sequence, chapterUrl) {
// 从 sequence 开始把操作接龙起来
return sequence.then(function() {
return getJSON(chapterUrl);
}).then(function(chapter) {
addHtmlToPage(chapter.html);
});
}, Promise.resolve());
  1. 我们希望同时下载所有章节,全部完成后一次搞定,正好就有这么个 API:
Promise.all(arrayOfPromises).then(function(arrayOfResults) {
//...
});

然而这样还是有提高空间。当第一章内容加载完毕我们可以立即填进页面,这样用户可以在其他加载任务尚未完成之前就开始阅读;当第三章到达的时候我们不动声色,第二章也到达之后我们再把第二章和第三章内容填入页面,以此类推。

  1. 为了达到这样的效果,我们同时请求所有的章节内容,然后创建一个序列依次将其填入页面:
getJSON('story.json').then(function(story) {
addHtmlToPage(story.heading);

// 把章节 URL 数组转换成对应的 Promise 数组
// 这样就可以并行加载它们
return story.chapterUrls.map(getJSON)
.reduce(function(sequence, chapterPromise) {
// 使用 reduce 把这些 Promise 接龙
// 以及将章节内容添加到页面
return sequence.then(function() {
// 等待当前 sequence 中所有章节和本章节的数据到达
return chapterPromise;
}).then(function(chapter) {
addHtmlToPage(chapter.html);
});
}, Promise.resolve());
}).then(function() {
addTextToPage("All done");
}).catch(function(err) {
// 捕获过程中的任何错误
addTextToPage("Argh, broken: " + err.message);
}).then(function() {
document.querySelector('.spinner').style.display = 'none';
});
%23Javascript%u4E4BPromise%0A@%28%u5B66%u4E60%u7B14%u8BB0%29%5Bjavascript%2C%20promise%5D%0A%0A%5BTOC%5D%0A%0APromise%u7684%u610F%u601D%u662F%u7EA6%u5B9A%uFF0C%u76F8%u5BF9%u5E94%u7684%u8BCD%u662FDefer%uFF0C%u662F%u63A8%u8FDF%u3002%u4ED6%u7684%u7528%u610F%u662F%u5728%u5F02%u6B65%u7684JavaScript%u4E16%u754C%u5F15%u5165%u540C%u6B65%u5199%u6CD5%u3002%u770B%u770B%u4E0B%u9762%u7684%u201C%u90AA%u6076%u91D1%u5B57%u5854%u201D%u3002%0A%60%60%60javascript%0Avar%20fs%20%3D%20require%28%27fs%27%29%3B%0Afs.readFile%28%27sample01.txt%27%2C%20%27utf8%27%2C%20function%20%28err%2C%20data%29%20%7B%0A%20%20%20%20fs.readFile%28%27sample02.txt%27%2C%20%27utf8%27%2C%20function%20%28err%2Cdata%29%20%7B%0A%20%20%20%20%20%20%20%20fs.readFile%28%27sample03.txt%27%2C%20%27utf8%27%2C%20function%20%28err%2C%20data%29%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20fs.readFile%28%27sample04.txt%27%2C%20%27utf8%27%2C%20function%20%28err%2C%20data%29%20%7B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%29%3B%0A%20%20%20%20%20%20%20%20%7D%29%3B%0A%20%20%20%20%7D%29%3B%0A%7D%29%3B%0A%60%60%60%0A%u65E2%u96BE%u770B%u53C8%u96BE%u7528%u3002%u6709%u4E86Promise%u4EE5%u540E%uFF0C%u8FD9%u79CD%u5D4C%u5957%u7684%u91D1%u5B57%u5854%u4EE3%u7801%u5C31%u53D8%u6210%u5E73%u7EA7%u7684%u94FE%u5F0F%u4EE3%u7801%u3002%u800C%u4E14%u53EF%u4EE5%u4FDD%u8BC1%u8C03%u7528%u7684%u987A%u5E8F%u3002%0A%0AJavaScript%u5B98%u65B9%u57282013%u5E74%u6B63%u5F0F%u5BA3%u5E03%u652F%u6301Promise%u8BED%u6CD5%uFF0C%u5C3D%u7BA1%u5728%u5F88%u591A%u6D4F%u89C8%u5668%u7AEF%uFF0C%u4ECD%u7136%u6709%u4E0D%u517C%u5BB9%u7684%u60C5%u51B5%u51FA%u73B0%u3002%u4E0D%u8FC7%u53EF%u89C1%u8FD9%u662F%u5927%u52BF%u6240%u8D8B%u3002%u5728%u5B98%u65B9%u5BA3%u5E03%u652F%u6301%u4E4B%u524D%uFF0C%u5DF2%u7ECF%u6709%u5F88%u591A%u7B2C%u4E09%u65B9%u5E93%u505A%u4E86polyfill%uFF0C%u5C31%u662F%u5BF9%u539F%u6709%u51FD%u6570%u8FDB%u884C%u4E86%u5305%u88C5%uFF0C%u4EE5%u4F7F%u5176%u652F%u6301Promise%u3002%u4F8B%u5982%u4E0B%u9762%u7684%u5E93%uFF1A%0A-%20Q%0A-%20when%0A-%20WinJS%0A-%20RSVP.js%0A%0A%u4E0A%u9762%u8FD9%u4E9B%u5E93%u548C%20JavaScript%20%u539F%u751F%20Promise%20%u90FD%u9075%u5B88%u4E00%u4E2A%u901A%u7528%u7684%u3001%u6807%u51C6%u5316%u7684%u89C4%u8303%uFF1A%5BPromises/A+%5D%28https%3A//github.com/promises-aplus/promises-spec%29%u3002jQuery%u7684Promise-Defer%u7684%u5B9E%u73B0%u4E0D%u7B26%u5408%u8FD9%u4E2A%u6807%u51C6%uFF0C%u4F7F%u7528%u9700%u8C28%u614E%u3002%0A%0A%u53E6%u5916%u5404%u79CDPromise%u7684%u5B9E%u73B0%u867D%u6982%u5FF5%u4E00%u81F4%uFF0C%u4F46%u4ECD%u4E0D%u5C3D%u76F8%u540C%u3002%u770B%u4E86%u5F88%u591A%u6587%u7AE0%uFF0C%u4ECD%u7136%u4E91%u91CC%u96FE%u91CC%u3002%u521A%u5F00%u59CB%u63A5%u89E6%u7684%u662F%u8FD9%u4E00%u7BC7%u6587%u7AE0%uFF1A%0A-%20%5BES6%20JavaScript%20Promise%u7684%u611F%u6027%u8BA4%u77E5%5D%28https%3A//www.evernote.com/shard/s24/nl/2724128/9fc4fe1b-7fe2-443c-81cf-ab053fa387e6%29%0A%u8BB2%u7684%u6709%u70B9%u5570%u55E6%uFF0C%u6587%u672B%u7F57%u5217%u7684%u53C2%u8003%u6587%u732E%u770B%u8D77%u6765%u8FD8%u4E0D%u9519%u3002%u540E%u6765%u9646%u9646%u7EED%u7EED%u53C8google%u4E86%u5F88%u591A%u6587%u7AE0%uFF0C%u8BB2%u7684%u4E5F%u4E0D%u5C3D%u76F8%u540C%uFF0C%u7406%u89E3%u4E0D%u80FD%u3002%u518D%u540E%u6765%u770B%u5230%u4E86%u4E0B%u9762%u8FD9%u4E00%u7BC7%u6587%u7AE0%u52A0%u4E00%u672C%u5728%u7EBF%u4E66%uFF0C%u611F%u89C9%u5F88%u4E0D%u9519%u3002%0A-%20%5BJavaScript%20Promises%5D%28https%3A//www.evernote.com/shard/s24/nl/2724128/bb1e3fc0-b85b-4fcf-a370-e13ba0573d6e%29%0A-%20%5BJavaScript%20Promise%u8FF7%u4F60%u4E66%uFF08%u4E2D%u6587%u7248%uFF09%5D%28http%3A//liubin.org/promises-book/%23ch2-promise-resolve%29%0A%0A%23%23%20Promise%u7684%u672F%u8BED%0A-%20%u80AF%u5B9A%uFF08fulfilled%uFF09%20%0A%09-%20%u8BE5%20Promise%20%u5BF9%u5E94%u7684%u64CD%u4F5C%u6210%u529F%u4E86%20%0A-%20%u5426%u5B9A%uFF08rejected%uFF09%20%0A%09-%20%u8BE5%20Promise%20%u5BF9%u5E94%u7684%u64CD%u4F5C%u5931%u8D25%u4E86%20%0A-%20%u7B49%u5F85%uFF08pending%uFF09%20%0A%09-%20%u8FD8%u6CA1%u6709%u5F97%u5230%u80AF%u5B9A%u6216%u8005%u5426%u5B9A%u7ED3%u679C%uFF0C%u8FDB%u884C%u4E2D%20%0A-%20%u7ED3%u675F%uFF08settled%uFF09%20%0A%09-%20%u5DF2%u7ECF%u80AF%u5B9A%u6216%u8005%u5426%u5B9A%u4E86%0A-%20%u7C7B%20Promise%20%28thenable%29%0A%09-%20%u4E00%u4E2A%u5BF9%u8C61%u662F%u5426%u662F%u201C%u7C7B%20Promise%u201D%uFF08%u62E5%u6709%u540D%u4E3A%u201Cthen%u201D%u7684%u65B9%u6CD5%uFF09%u7684%u3002%0A%0A%u521D%u59CB%u72B6%u6001%u662Fpending%uFF0Csettled%3Dfulfilled%20or%20rejected%uFF0C%u72B6%u6001%u8F6C%u6362%u53EA%u6709%u4E00%u6B21%uFF0C%u800C%u4E14%u4E0D%u80FD%u9006%u8F6C%u3002%0A%0A%23%23%u5982%u4F55%u4F7F%u7528Promise%0A%60%60%60javascript%0Avar%20promise%20%3D%20new%20Promise%28function%28resolve%2C%20reject%29%20%7B%0A%20%20//%20%u505A%u4E00%u4E9B%u5F02%u6B65%u64CD%u4F5C%u7684%u4E8B%u60C5%uFF0C%u7136%u540E%u2026%u2026%0A%0A%20%20if%20%28/*%20%u4E00%u5207%u6B63%u5E38%20*/%29%20%7B%0A%20%20%20%20resolve%28%22Stuff%20worked%21%22%29%3B%0A%20%20%7D%0A%20%20else%20%7B%0A%20%20%20%20reject%28Error%28%22It%20broke%22%29%29%3B%0A%20%20%7D%0A%7D%29%0A%0Apromise.then%28function%28result%29%20%7B%0A%20%20console.log%28result%29%3B%20//%20%u201C%u5B8C%u7F8E%uFF01%u201D%0A%7D%2C%20function%28err%29%20%7B%0A%20%20console.log%28err%29%3B%20//%20Error%3A%20%22%u51FA%u95EE%u9898%u4E86%22%0A%7D%29%3B%3B%0A%60%60%60%0A%23%23%23%u6784%u9020%u51FD%u6570%0A%3E%u6784%u9020%u5668%u63A5%u53D7%u4E00%u4E2A%u51FD%u6570%u4F5C%u4E3A%u53C2%u6570%uFF0C%u5B83%u4F1A%u4F20%u9012%u7ED9%u8FD9%u4E2A%u56DE%u8C03%u51FD%u6570%u4E24%u4E2A%u53D8%u91CF%20resolve%20%u548C%20reject%u3002%u5728%u56DE%u8C03%u51FD%u6570%u4E2D%u505A%u4E00%u4E9B%u5F02%u6B65%u64CD%u4F5C%uFF0C%u6210%u529F%u4E4B%u540E%u8C03%u7528%20resolve%uFF0C%u5426%u5219%u8C03%u7528%20reject%u3002%0A%0A%u4E0A%u9762%u8FD9%u6BB5%u8BDD%u7684%u610F%u601D%u662F%uFF0C%0A-%20%u9996%u5148%uFF0CPromise%u5BF9%u8C61%u7528%u4E00%u4E2A%u56DE%u8C03%u51FD%u6570%u6765%u6784%u9020%u3002%0A-%20%u5176%u6B21%uFF0C%u770B%u4E0B%u9762%u8FD9%u6BB5code%0A%09-%20%u8FD9%u4E2A%u56DE%u8C03%u51FD%u6570%u6709%u4E24%u4E2A%u53C2%u6570%uFF0C%u8FD9%u4E24%u4E2A%u53C2%u6570%u7531Promise%u6765%u8D1F%u8D23%u4F20%u9012%28resolve%26%20reject%29%u3002%0A%09-%20%u56DE%u8C03%u51FD%u6570%u4E2D%u5B9A%u4E49%u4E00%u4E9B%u81EA%u5DF1%u9700%u8981%u505A%u7684%u5F02%u6B65%u64CD%u4F5C%28asyncOp%29%u3002%0A%09-%20%u5F02%u6B65%u64CD%u4F5C%u7ED3%u675F%u65F6%uFF0C%u81EA%u884C%u5224%u65AD%u6210%u529F%u5931%u8D25%uFF0C%u5931%u8D25%u8C03%u7528reject%u51FD%u6570%uFF0C%u6210%u529F%u8C03%u7528resolve%u51FD%u6570%u3002%0A%09-%20Promise%u4F7F%u7528%u65F6%u81F3%u5C11%u4E0E%u4E00%u4E2Athen%u914D%u5BF9%uFF0C%u5728then%u91CC%u9762%u505AasyncOp%u4E4B%u540E%u60F3%u505A%u7684%u4E8B%u60C5%u3002Promise%u91CC%u9762%u7684%u7ED3%u679C%u901A%u8FC7resolve%u548Creject%u4F20%u9012%u7684%u53C2%u6570%uFF08err%26res%uFF09%u4F20%u5230then%u91CC%u3002%0A%60%60%60javascript%0Afunction%20polyFill%28%29%7B%0A%20%20return%20new%20Promise%28function%20%28resolve%2C%20reject%29%7B%0A%20%20%20%20fs.asyncOp%28function%20%28err%2C%20res%29%7B%0A%20%20%20%20%20%20if%20%28err%29%20%0A%09%20%20%20%20%20%20reject%28err%29%3B%0A%20%20%20%20%20%20else%20%0A%09%20%20%20%20%20%20resolve%28res%29%3B%0A%20%20%20%20%7D%29%3B%0A%20%20%7D%29%3B%0A%7D%0ApolyFill%28%29.then%28resolveFunc%2C%20rejectFunc%29%3B%0A%60%60%60%0A%23%23%23resolve%20%26%20reject%0A%u8FD9%u4E24%u4E2A%u662FPromise%u7C7B%u7684%u6210%u5458%u51FD%u6570%uFF0C%u4F7F%u7528%u7684%u65F6%u5019%uFF0C%u4E5F%u53EF%u4EE5%u50CF%u4E0B%u9762%u8FD9%u6837%u8C03%u7528%uFF1A%0A%60%60%60javascript%0Apromise.resolve%28val%29.then%28...%29%3B%0Apromise.reject%28Error%28err%29%29.then%28...%29%3B%0A%60%60%60%0A%u8FD9%u6837%u7684%u8C03%u7528%u548C%u4E0B%u9762%u7684%u5E38%u89C4%u8C03%u7528%u662F%u7B49%u6548%u7684%uFF1A%0A%60%60%60javascript%0Avar%20promise%20%3D%20Promise%28function%28resolve%2C%20reject%29%7B%0A%09if%20%28good%29%7B%0A%09%09resolve%28val%29%3B%09%0A%09%7D%0A%09else%20%7B%0A%09%09reject%28Error%28err%29%29%3B%0A%09%7D%0A%7D%29%3B%0Apromise.then%28...%29%3B%0A%60%60%60%0A%u5404%u8DEF%u8D34%u5B50%u548C%u89E3%u8BF4%u4E2D%u90FD%u9C9C%u5C11%u63D0%u53CAresolve%u548Creject%u51FD%u6570%u7684%u53C2%u6570%u662F%u4EC0%u4E48%uFF0C%u6709%u4EC0%u4E48%u7528%u3002%u6211%u4E4B%u524D%u5F15%u7528%u7684%u4E24%u4E2A%u94FE%u63A5%u90FD%u6709%u8BF4%u5230%uFF0C%u4F46%u6211%u611F%u89C9%u8BB2%u7684%u90FD%u4E0D%u662F%u5F88%u6E05%u695A%u3002%u4E0B%u9762%u7406%u4E00%u4E0B%u6211%u7684%u601D%u8DEF%uFF1A%0A%3E%20resolve%0A%3E%20-%20resolve%u603B%u662F%u8FD4%u56DE%u4E00%u4E2APromise%u5BF9%u8C61%0A%3E%20-%20%u63A5%u6536%u5230promise%u5BF9%u8C61%u53C2%u6570%u7684%u65F6%u5019%2C%20%u8FD4%u56DE%u7684%u8FD8%u662F%u63A5%u6536%u5230%u7684promise%u5BF9%u8C61%uFF0C%u800C%u4E14%u4E0B%u4E00%u4E2Athen%u4F1A%u7B49%u5230%u8FD9%u4E2APromise%u5B9A%u4E49%u7684%u5F02%u6B65%u64CD%u4F5C%u7ED3%u675F%u65F6%u624D%u4F1A%u8FD0%u884C%u3002%0A%3E%20-%20%u63A5%u6536%u5230thenable%u7C7B%u578B%u7684%u5BF9%u8C61%u7684%u65F6%u5019%2C%20%u8FD4%u56DE%u4E00%u4E2A%u65B0%u7684promise%u5BF9%u8C61%uFF0C%u8FD9%u4E2A%u5BF9%u8C61%u5177%u6709%u4E00%u4E2A%20then%20%u65B9%u6CD5%0A%3E%20-%20%u63A5%u6536%u7684%u53C2%u6570%u4E3A%u5176%u4ED6%u7C7B%u578B%u7684%u65F6%u5019%uFF08%u5305%u62ECJavaScript%u5BF9%u6216null%u7B49%uFF09%2C%20%u8FD4%u56DE%u4E00%u4E2A%u5C06%u8BE5%u5BF9%u8C61%u4F5C%u4E3A%u503C%u7684%u65B0promise%u5BF9%u8C61%uFF0C%u8FD9%u4E2A%u503C%u4F1A%u88AB%u5F53%u4F5C%u53C2%u6570%u4F20%u5230%u4E0B%u4E00%u4E2A%u5B9A%u4E49%u4E86resolve%u51FD%u6570%u53C2%u6570%u7684then%u91CC%u9762%u3002%0A%3E%20reject%0A%3E%20%20-%20reject%u4E0Eresolve%u7C7B%u4F3C%uFF0C%u8FD4%u56DE%u7684%u4E5F%u662F%u4E00%u4E2APromise%u5BF9%u8C61%0A%3E%20%20-%20reject%u901A%u5E38%u4F20%u5165%u4E00%u4E2A%60Error%28%29%60%u5BF9%u8C61%u3002%u8FD9%u4E0D%u662F%u5FC5%u987B%u7684%uFF0C%u4F46Error%u5BF9%u8C61%u5305%u542B%u4E86%u8C03%u7528%u6808%uFF0C%u65B9%u4FBF%u8C03%u8BD5%u3002%0A%0A%23%23%23%u9519%u8BEF%u5904%u7406%0A%u4E0A%u9762%u63D0%u5230%u4E86reject%u5DF2%u7ECF%u6D89%u53CA%u5230%u4E00%u90E8%u5206%u9519%u8BEF%u5904%u7406%u4E86%u3002%u5728Promise%u91CC%u9762%uFF0C%u6211%u4EEC%u8FD8%u53EF%u4EE5%u7528catch%u3002%0A%60%60%60javascript%0Aget%28%27story.json%27%29.then%28function%28response%29%20%7B%20%0A%09console.log%28%22Success%21%22%2C%20response%29%3B%0A%7D%29.catch%28function%28error%29%20%7B%0A%09console.log%28%22Failed%21%22%2C%20error%29%3B%0A%7D%29%3B%0A%60%60%60%0A%u5728%5BJavaScript%20Promises%5D%28https%3A//www.evernote.com/shard/s24/nl/2724128/bb1e3fc0-b85b-4fcf-a370-e13ba0573d6e%29%u8FD9%u91CC%u63D0%u5230%uFF1A%0A%3E%u8FD9%u91CC%u7684%20catch%20%u5E76%u65E0%u4EFB%u4F55%u7279%u6B8A%u4E4B%u5904%uFF0C%u53EA%u662F%20then%28undefined%2C%20func%29%20%u7684%u8BED%u6CD5%u7CD6%u8863%uFF0C%u66F4%u76F4%u89C2%u4E00%u70B9%u800C%u5DF2%u3002catch%u76F8%u5F53%u4E8E%uFF1A%0A%60%60%60javascript%0Aget%28%27story.json%27%29.then%28function%28response%29%20%7B%0A%20%20console.log%28%22Success%21%22%2C%20response%29%3B%0A%7D%29.then%28undefined%2C%20function%28error%29%20%7B%0A%20%20console.log%28%22Failed%21%22%2C%20error%29%3B%0A%7D%29%3B%0A%60%60%60%0A%u603B%u7ED3%u4E00%u4E0B%uFF1A%0A%u5728Promise%u4E2D%u7684%u4EFB%u4F55%u9519%u8BEF%uFF0C%u5305%u62EC%uFF1A%0A-%20reject%u8C03%u7528%0A-%20Promise%u4E2D%u7684%u629B%u51FA%u7684%u5F02%u5E38%0A%0A%u4F1A%u843D%u5230Promise%u540E%u9762%u7B2C%u4E00%u4E2A%u5B9A%u4E49%u4E86reject%u51FD%u6570%u53C2%u6570%u7684then%u5206%u652F%u91CC%u3002%0A%0A%23%23%u9AD8%u7EA7Promise%0A%23%23%23%u521B%u5EFA%u5E8F%u5217%0A%u5728%5BJavaScript%20Promises%5D%28https%3A//www.evernote.com/shard/s24/nl/2724128/bb1e3fc0-b85b-4fcf-a370-e13ba0573d6e%29%u4E2D%uFF0C%u8BE6%u7EC6%u89E3%u91CA%u4E86%u5982%u4F55%u64CD%u4F5C%u4E00%u7EC4Promise%uFF0C%u4ECE%u771F%u6B63%u7684%u7528%u4E00%u7EC4Promise%u5230%u4F7F%u7528Promise.all%u3002%u8FD9%u91CC%u8BB2%u7684%u5F88%u597D%u4E86%uFF0C%u6211%u76F4%u63A5%u62F7%u8FC7%u6765%u3002%0A1.%20%u6700%u76F4%u63A5%u7684%u6570%u7EC4forEach%0A%60%60%60javascript%0A//%20%u4ECE%u4E00%u4E2A%u5B8C%u6210%u72B6%u6001%u7684%20Promise%20%u5F00%u59CB%0Avar%20sequence%20%3D%20Promise.resolve%28%29%3B%0A%0A//%20%u904D%u5386%u6240%u6709%u7AE0%u8282%u7684%20url%0Astory.chapterUrls.forEach%28function%28chapterUrl%29%20%7B%0A%20%20//%20%u4ECE%20sequence%20%u5F00%u59CB%u628A%u64CD%u4F5C%u63A5%u9F99%u8D77%u6765%0A%20%20sequence%20%3D%20sequence.then%28function%28%29%20%7B%0A%20%20%20%20return%20getJSON%28chapterUrl%29%3B%0A%20%20%7D%29.then%28function%28chapter%29%20%7B%0A%20%20%20%20addHtmlToPage%28chapter.html%29%3B%0A%20%20%7D%29%3B%0A%7D%29%3B%0A%60%60%60%0A2.%20%u7528%20%5Barray.reduce%5D%28https%3A//developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce%29%20%u7CBE%u7B80%u4E00%u4E0B%u4E0A%u9762%u7684%u4EE3%u7801%uFF1A%0A%60%60%60javascript%0A//%20%u904D%u5386%u6240%u6709%u7AE0%u8282%u7684%20url%0Astory.chapterUrls.reduce%28function%28sequence%2C%20chapterUrl%29%20%7B%0A%20%20//%20%u4ECE%20sequence%20%u5F00%u59CB%u628A%u64CD%u4F5C%u63A5%u9F99%u8D77%u6765%0A%20%20return%20sequence.then%28function%28%29%20%7B%0A%20%20%20%20return%20getJSON%28chapterUrl%29%3B%0A%20%20%7D%29.then%28function%28chapter%29%20%7B%0A%20%20%20%20addHtmlToPage%28chapter.html%29%3B%0A%20%20%7D%29%3B%0A%7D%2C%20Promise.resolve%28%29%29%3B%0A%60%60%60%0A3.%20%u6211%u4EEC%u5E0C%u671B%u540C%u65F6%u4E0B%u8F7D%u6240%u6709%u7AE0%u8282%uFF0C%u5168%u90E8%u5B8C%u6210%u540E%u4E00%u6B21%u641E%u5B9A%uFF0C%u6B63%u597D%u5C31%u6709%u8FD9%u4E48%u4E2A%20API%uFF1A%0A%60%60%60javascript%0APromise.all%28arrayOfPromises%29.then%28function%28arrayOfResults%29%20%7B%0A%20%20//...%0A%7D%29%3B%0A%60%60%60%0A%u7136%u800C%u8FD9%u6837%u8FD8%u662F%u6709%u63D0%u9AD8%u7A7A%u95F4%u3002%u5F53%u7B2C%u4E00%u7AE0%u5185%u5BB9%u52A0%u8F7D%u5B8C%u6BD5%u6211%u4EEC%u53EF%u4EE5%u7ACB%u5373%u586B%u8FDB%u9875%u9762%uFF0C%u8FD9%u6837%u7528%u6237%u53EF%u4EE5%u5728%u5176%u4ED6%u52A0%u8F7D%u4EFB%u52A1%u5C1A%u672A%u5B8C%u6210%u4E4B%u524D%u5C31%u5F00%u59CB%u9605%u8BFB%uFF1B%u5F53%u7B2C%u4E09%u7AE0%u5230%u8FBE%u7684%u65F6%u5019%u6211%u4EEC%u4E0D%u52A8%u58F0%u8272%uFF0C%u7B2C%u4E8C%u7AE0%u4E5F%u5230%u8FBE%u4E4B%u540E%u6211%u4EEC%u518D%u628A%u7B2C%u4E8C%u7AE0%u548C%u7B2C%u4E09%u7AE0%u5185%u5BB9%u586B%u5165%u9875%u9762%uFF0C%u4EE5%u6B64%u7C7B%u63A8%u3002%0A%0A4.%20%u4E3A%u4E86%u8FBE%u5230%u8FD9%u6837%u7684%u6548%u679C%uFF0C%u6211%u4EEC%u540C%u65F6%u8BF7%u6C42%u6240%u6709%u7684%u7AE0%u8282%u5185%u5BB9%uFF0C%u7136%u540E%u521B%u5EFA%u4E00%u4E2A%u5E8F%u5217%u4F9D%u6B21%u5C06%u5176%u586B%u5165%u9875%u9762%uFF1A%0A%60%60%60javascript%0AgetJSON%28%27story.json%27%29.then%28function%28story%29%20%7B%0A%20%20addHtmlToPage%28story.heading%29%3B%0A%0A%20%20//%20%u628A%u7AE0%u8282%20URL%20%u6570%u7EC4%u8F6C%u6362%u6210%u5BF9%u5E94%u7684%20Promise%20%u6570%u7EC4%0A%20%20//%20%u8FD9%u6837%u5C31%u53EF%u4EE5%u5E76%u884C%u52A0%u8F7D%u5B83%u4EEC%0A%20%20return%20story.chapterUrls.map%28getJSON%29%0A%20%20%20%20.reduce%28function%28sequence%2C%20chapterPromise%29%20%7B%0A%20%20%20%20%20%20//%20%u4F7F%u7528%20reduce%20%u628A%u8FD9%u4E9B%20Promise%20%u63A5%u9F99%0A%20%20%20%20%20%20//%20%u4EE5%u53CA%u5C06%u7AE0%u8282%u5185%u5BB9%u6DFB%u52A0%u5230%u9875%u9762%0A%20%20%20%20%20%20return%20sequence.then%28function%28%29%20%7B%0A%20%20%20%20%20%20%20%20//%20%u7B49%u5F85%u5F53%u524D%20sequence%20%u4E2D%u6240%u6709%u7AE0%u8282%u548C%u672C%u7AE0%u8282%u7684%u6570%u636E%u5230%u8FBE%0A%20%20%20%20%20%20%20%20return%20chapterPromise%3B%0A%20%20%20%20%20%20%7D%29.then%28function%28chapter%29%20%7B%0A%20%20%20%20%20%20%20%20addHtmlToPage%28chapter.html%29%3B%0A%20%20%20%20%20%20%7D%29%3B%0A%20%20%20%20%7D%2C%20Promise.resolve%28%29%29%3B%0A%7D%29.then%28function%28%29%20%7B%0A%20%20addTextToPage%28%22All%20done%22%29%3B%0A%7D%29.catch%28function%28err%29%20%7B%0A%20%20//%20%u6355%u83B7%u8FC7%u7A0B%u4E2D%u7684%u4EFB%u4F55%u9519%u8BEF%0A%20%20addTextToPage%28%22Argh%2C%20broken%3A%20%22%20+%20err.message%29%3B%0A%7D%29.then%28function%28%29%20%7B%0A%20%20document.querySelector%28%27.spinner%27%29.style.display%20%3D%20%27none%27%3B%0A%7D%29%3B%0A%60%60%60%0A%0A

Edit

学习笔记 javascript 面向对象

写这篇笔记,是阅读了以下这个系列三个网页,想摘录一些东西,以后也方便回忆。

作者由浅入深,娓娓道来,讲的很透彻。文中讲了很多种javascript实现面向对象的方法,和各自的优劣。我只记录作者认为最好的,和我认为最常见的。

在开始之前,有一些heads up。

  • JS本身是不支持OO的,但是运用其自身的各种特性,仍然能实现类的封装和继承,这是OO的精华部分
  • JS中所有物件都是对象
    • function定义的函数是Function类的对象
    • 字典是Object类的对象
    • 数组是Array类的对象
    • 简单变量是Number类对象

类封装的Prototype模式

看下面的code:

1.function Cat(name,color){
2. this.name = name;
3. this.color = color;
4.}
5.
6.Cat.prototype.type = "猫科动物";
7.Cat.prototype.eat = function(){alert("吃老鼠")};
8.  
9.var cat1 = new Cat("大毛","黄色");
10.var cat2 = new Cat("二毛","黑色");
11.
12.alert(cat1.type); // 猫科动物
13.cat1.eat(); // 吃老鼠

每个函数都有一个prototype上,每个prototype指向另一个对象。不变的属性,方法可以定义在这个prototype上,从而达到减少重复,节省内存的作用。

express中的model定义就是用的这种方式。

继承

继承有两种,通过构造函数继承和通过对象拷贝继承。javascript是面向函数编程,这个函数对于类定义来说其实就是构造函数。上面讲的prototype就有点继承的意思了。但我们的prototype并没有一个函数与之对应,即没有构造函数,所以它不能称其类,也没法实例化。所以不能称其为继承。

通过prototype继承

那要继承就要有构造函数

1.function Animal(){
2. this.species = "动物";
3.}
4.Cat.prototype = new Animal();
5.Cat.prototype.constructor = Cat;
6.var cat1 = new Cat("大毛","黄色");
7.alert(cat1.species); // 动物

这样就是真正的继承了,Cat继承自Animal。这里有一个小把戏:Cat.prototype.constructor = Cat;
这里牵涉到一个很饶人的小问题,也是很多人在讨论的,也是jsoo躲不开的问题,就是:

constructor的作用到底是什么?
首先,在js中,所有的物件皆为对象。
其次每一个对象都有他的构造函数,由object.constructor指明。
最后,所有object.constructor均等于object.prototype.constructor。看下面的REPL的代码:

1.> function Cat(){}
2.> function Animal(){}
3.> Cat.prototype=new Animal
4.> cat = new Cat()
5.Animal {} // 看这里的结果
6.> cat.constructor===Animal // 再看这一行
7.true
8.> Cat.constructor === Function
9.true

可见,如果修改了类的prototype,它自己对象的constructor就会指向其他的地方。问题来了:

这么做有什么问题呢?

这让我百思不得其解。引用知乎上一个回答:

constructor属性不影响任何JavaScript的内部属性。instanceof检测对象的原型链,通常你是无法修改的(不过某些引擎通过私有的proto属性暴露出来)。
constructor其实没有什么用处,只是JavaScript语言设计的历史遗留物。由于constructor属性是可以变更的,所以未必真的指向对象的构造函数,只是一个提示。不过,从编程习惯上,我们应该尽量让对象的constructor指向其构造函数,以维持这个惯例

用instanceof看不出什么差别,可能也印证了这位知乎大牛的观点。

1.> cat = new Cat()
2.Animal {}
3.> cat instanceof Cat
4.true
5.> cat instanceof Animal
6.true
7.> Cat.prototype.constructor=Cat
8.[Function: Cat]
9.> cat=new Cat()
10.Cat {}
11.> cat instanceof Cat
12.true
13.> cat instanceof Animal
14.true

再回到我们之前的例子Cat.prototype = new Animal();这一句也隐含的做了Cat.prototype.constructor = Animal。导致cat.constructor === Cat.prototype.constructor
看起来有点乱,cat看起来是由Animal来构造的了。
我的理解是:

一般使用不会有问题,当在一些高级应用场合,要用到反射的时候,如果constructor设置不对会引起不必要的麻烦。

根据prototype继承的方法,Javascript面向对象编程(二):构造函数的继承又提出了两种改进型的继承方法,分别是

  • 直接继承prototype
  • 利用空对象作为中介

直接继承prototype

类的prototype是这个类稳定的部分,当我们想继承的时候,其实就是想继承类中稳定的部分。所以这个方法就是直接继承父类的prototype:

1.function Animal(){
2. this.species = "动物";
3.}
4.Cat.prototype = Aninal.prototype;
5.Cat.prototype.constructor = Cat;

虽然听起来有理,不过真的这么做还是挺奇怪的。子类和父类的prototype指向了同一个对象(JSOO中,万物皆对象,和C++/Java中的对象概念有稍许区别)。
而对Cat.prototype.constructor的修改直接影响了Animal.constructor。

结论就是这个方法不好,不能用。

利用空对象作为中介

直接上代码:

1.var F = function(){};
2.F.prototype = Animal.prototype;
3.Cat.prototype = new F();
4.Cat.prototype.constructor = Cat;

这样的做法隔离了父类和子类。

  • 优点是节省内存
    • 因为F是空函数,定义Cat.prototype的时候,new的是F比new一个Animal肯定是要节省内存的。
  • 缺点是不太直观。

不过据说有些JS库(YUI)的OO就是用的这种模式。

使用方法:

1.function extend(Child, Parent) {
2. var F = function(){};
3. F.prototype = Parent.prototype;
4. Child.prototype = new F();
5. Child.prototype.constructor = Child;
6. Child.uber = Parent.prototype;
7.}
8.
9.extend(Cat,Animal);
10.var cat1 = new Cat("大毛","黄色");
11.alert(cat1.species); // 动物

这里用到了一个类的uber属性。这是什么?

意思是为子对象设一个uber属性,这个属性直接指向父对象的prototype属性。(uber是一个德语词,意思是”向上”、”上一层”。)这等于在子对象上打开一条通道,可以直接调用父对象的方法。这一行放在这里,只是为了实现继承的完备性,纯属备用性质。

我的理解,给子类能访问父类函数的机会。为什么叫uber,我觉得应该是约定俗成的。

1.> function Cat2(){}
2.> 'uber' in Cat2
3.false
4.>

最后还有一个另类,通过在构造函数里面拷贝父类的属性达到继承的效果,这个和后一章的继承方法有异曲同工之效。区别是这里是在构造函数里做拷贝。

1.function extend2(Child, Parent) {
2. var p = Parent.prototype;
3. var c = Child.prototype;
4. for (var i in p) {
5. c[i] = p[i];
6. }
7. c.uber = p;
8.}
9.
10.extend2(Cat, Animal);
11.var cat1 = new Cat("大毛","黄色");
12.alert(cat1.species); // 动物

注意Javascript面向对象编程(二):构造函数的继承中虽然没有提到,但这里仍然会有深拷贝,浅拷贝的问题。

通过拷贝继承

这里解决的问题,和标准的OO处理的问题不同。这里要处理的问题,是让两个普通对象之间有继承关系:

1.var Chinese = {
2. nation:'中国'
3.};
4.var Doctor ={
5. career:'医生'
6.}

注意这里都是普通的字典类型对象,并不像前一章说的是用function构造出来的类,再用new构造的对象。
现在要解决的问题,是’中国医生’要如何定义。这里很容易想到就是把Chinese里面的属性拷给Doctor就好了嘛。那这一章讲的就是这个内容。

object()方法

这个方法其实还是在用构造函数。

1.function object(o) {
2. function F() {}
3. F.prototype = o;
4. return new F();
5.}
6.
7.var Doctor = object(Chinese);
8.Doctor.career = '医生';
9.alert(Doctor.nation); //中国

根据上一章我们讲的构造函数的知识,可以知道:

  • Doctor是F类的对象
  • F类通过prototype继承了Chinese对象
  • 有下面的等式
1.> Doctor.constructor === Chinese.constructor
2.true

Doctor是F类的对象,F类的prototype是Chinese对象,Doctor的constructor指向他原型的contructor。
非要和构造函数扯上关系,是想对上一章的知识活学活用。作者把这个方法列在这里的用意,其实是这里的方法跟构造函数,constructor没半毛钱关系。只是单纯的将Chinese通过prototype传给Doctor

后面的方法就从形式上也和构造函数也没半毛钱关系了。

  • 浅拷贝
  • 深拷贝
    不讲浅拷贝,因为深拷贝包含了浅拷贝

深拷贝

其实拷贝对于解决这个问题,本身就是最直觉的方案。直接看code:

1.function deepCopy(p, c) {
2. var c = c || {};
3. for (var i in p) {
4. if (typeof p[i] === 'object') {
5. c[i] = (p[i].constructor === Array) ? [] : {};
6. deepCopy(p[i], c[i]);
7. } else {
8. c[i] = p[i];
9. }
10. }
11. return c;
12.}
13.var Doctor = deepCopy(Chinese);

要用深拷贝的原因是Chinese对象内部可能有成员的值是数组或其他对象。jQuery库用的就是这种继承方法。

%23Javascript%u9762%u5411%u5BF9%u8C61%u5165%u95E8%0A@%28%u5B66%u4E60%u7B14%u8BB0%29%5Bjavascript%2C%u9762%u5411%u5BF9%u8C61%5D%0A%0A%5BTOC%5D%0A%0A%u5199%u8FD9%u7BC7%u7B14%u8BB0%uFF0C%u662F%u9605%u8BFB%u4E86%u4EE5%u4E0B%u8FD9%u4E2A%u7CFB%u5217%u4E09%u4E2A%u7F51%u9875%uFF0C%u60F3%u6458%u5F55%u4E00%u4E9B%u4E1C%u897F%uFF0C%u4EE5%u540E%u4E5F%u65B9%u4FBF%u56DE%u5FC6%u3002%0A-%20%5BJavascript%20%u9762%u5411%u5BF9%u8C61%u7F16%u7A0B%uFF08%u4E00%uFF09%uFF1A%u5C01%u88C5%5D%28https%3A//www.evernote.com/shard/s24/nl/697376807/f579e44e-fb4a-4bc5-ac4e-4279f39cf00e/%29%0A-%20%5BJavascript%u9762%u5411%u5BF9%u8C61%u7F16%u7A0B%uFF08%u4E8C%uFF09%uFF1A%u6784%u9020%u51FD%u6570%u7684%u7EE7%u627F%5D%28https%3A//www.evernote.com/shard/s24/nl/697376807/9f56f8ec-b75a-469d-9320-d3d206766dc1/%29%0A-%20%5BJavascript%u9762%u5411%u5BF9%u8C61%u7F16%u7A0B%uFF08%u4E09%uFF09%uFF1A%u975E%u6784%u9020%u51FD%u6570%u7684%u7EE7%u627F%5D%28https%3A//www.evernote.com/shard/s24/nl/697376807/32cfbff4-0cef-44a2-adeb-6953cecc1158/%29%0A%0A%u4F5C%u8005%u7531%u6D45%u5165%u6DF1%uFF0C%u5A13%u5A13%u9053%u6765%uFF0C%u8BB2%u7684%u5F88%u900F%u5F7B%u3002%u6587%u4E2D%u8BB2%u4E86%u5F88%u591A%u79CDjavascript%u5B9E%u73B0%u9762%u5411%u5BF9%u8C61%u7684%u65B9%u6CD5%uFF0C%u548C%u5404%u81EA%u7684%u4F18%u52A3%u3002%u6211%u53EA%u8BB0%u5F55%u4F5C%u8005%u8BA4%u4E3A%u6700%u597D%u7684%uFF0C%u548C%u6211%u8BA4%u4E3A%u6700%u5E38%u89C1%u7684%u3002%0A%0A%u5728%u5F00%u59CB%u4E4B%u524D%uFF0C%u6709%u4E00%u4E9Bheads%20up%u3002%0A-%20JS%u672C%u8EAB%u662F%u4E0D%u652F%u6301OO%u7684%uFF0C%u4F46%u662F%u8FD0%u7528%u5176%u81EA%u8EAB%u7684%u5404%u79CD%u7279%u6027%uFF0C%u4ECD%u7136%u80FD%u5B9E%u73B0%u7C7B%u7684%u5C01%u88C5%u548C%u7EE7%u627F%uFF0C%u8FD9%u662FOO%u7684%u7CBE%u534E%u90E8%u5206%0A-%20JS%u4E2D%u6240%u6709%u7269%u4EF6%u90FD%u662F%u5BF9%u8C61%0A%09-%20function%u5B9A%u4E49%u7684%u51FD%u6570%u662FFunction%u7C7B%u7684%u5BF9%u8C61%0A%09-%20%u5B57%u5178%u662FObject%u7C7B%u7684%u5BF9%u8C61%0A%09-%20%u6570%u7EC4%u662FArray%u7C7B%u7684%u5BF9%u8C61%0A%09-%20%u7B80%u5355%u53D8%u91CF%u662FNumber%u7C7B%u5BF9%u8C61%0A%0A%23%23%u7C7B%u5C01%u88C5%u7684Prototype%u6A21%u5F0F%0A%u770B%u4E0B%u9762%u7684code%uFF1A%0A%60%60%60javascript%0Afunction%20Cat%28name%2Ccolor%29%7B%0A%09this.name%20%3D%20name%3B%0A%09this.color%20%3D%20color%3B%0A%7D%0A%0ACat.prototype.type%20%3D%20%22%u732B%u79D1%u52A8%u7269%22%3B%0ACat.prototype.eat%20%3D%20function%28%29%7Balert%28%22%u5403%u8001%u9F20%22%29%7D%3B%0A%u3000%u3000%0Avar%20cat1%20%3D%20new%20Cat%28%22%u5927%u6BDB%22%2C%22%u9EC4%u8272%22%29%3B%0Avar%20cat2%20%3D%20new%20Cat%28%22%u4E8C%u6BDB%22%2C%22%u9ED1%u8272%22%29%3B%0A%0Aalert%28cat1.type%29%3B%20//%20%u732B%u79D1%u52A8%u7269%0Acat1.eat%28%29%3B%20//%20%u5403%u8001%u9F20%0A%60%60%60%0A%u6BCF%u4E2A%u51FD%u6570%u90FD%u6709%u4E00%u4E2Aprototype%u4E0A%uFF0C%u6BCF%u4E2Aprototype%u6307%u5411%u53E6%u4E00%u4E2A%u5BF9%u8C61%u3002%u4E0D%u53D8%u7684%u5C5E%u6027%uFF0C%u65B9%u6CD5%u53EF%u4EE5%u5B9A%u4E49%u5728%u8FD9%u4E2Aprototype%u4E0A%uFF0C%u4ECE%u800C%u8FBE%u5230%u51CF%u5C11%u91CD%u590D%uFF0C%u8282%u7701%u5185%u5B58%u7684%u4F5C%u7528%u3002%0A%0Aexpress%u4E2D%u7684model%u5B9A%u4E49%u5C31%u662F%u7528%u7684%u8FD9%u79CD%u65B9%u5F0F%u3002%0A%0A%23%23%u7EE7%u627F%0A%u7EE7%u627F%u6709%u4E24%u79CD%uFF0C%u901A%u8FC7%u6784%u9020%u51FD%u6570%u7EE7%u627F%u548C%u901A%u8FC7%u5BF9%u8C61%u62F7%u8D1D%u7EE7%u627F%u3002javascript%u662F%u9762%u5411%u51FD%u6570%u7F16%u7A0B%uFF0C%u8FD9%u4E2A%u51FD%u6570%u5BF9%u4E8E%u7C7B%u5B9A%u4E49%u6765%u8BF4%u5176%u5B9E%u5C31%u662F%u6784%u9020%u51FD%u6570%u3002%u4E0A%u9762%u8BB2%u7684prototype%u5C31%u6709%u70B9%u7EE7%u627F%u7684%u610F%u601D%u4E86%u3002%u4F46%u6211%u4EEC%u7684prototype%u5E76%u6CA1%u6709%u4E00%u4E2A%u51FD%u6570%u4E0E%u4E4B%u5BF9%u5E94%uFF0C%u5373%u6CA1%u6709%u6784%u9020%u51FD%u6570%uFF0C%u6240%u4EE5%u5B83%u4E0D%u80FD%u79F0%u5176%u7C7B%uFF0C%u4E5F%u6CA1%u6CD5%u5B9E%u4F8B%u5316%u3002%u6240%u4EE5%u4E0D%u80FD%u79F0%u5176%u4E3A%u7EE7%u627F%u3002%0A%0A%23%23%23%u901A%u8FC7%u6784%u9020%u51FD%u6570%u7EE7%u627F%0A%23%23%23%23%u901A%u8FC7prototype%u7EE7%u627F%0A%u90A3%u8981%u7EE7%u627F%u5C31%u8981%u6709%u6784%u9020%u51FD%u6570%0A%60%60%60javascript%0Afunction%20Animal%28%29%7B%0A%09this.species%20%3D%20%22%u52A8%u7269%22%3B%0A%7D%0ACat.prototype%20%3D%20new%20Animal%28%29%3B%0ACat.prototype.constructor%20%3D%20Cat%3B%0Avar%20cat1%20%3D%20new%20Cat%28%22%u5927%u6BDB%22%2C%22%u9EC4%u8272%22%29%3B%0Aalert%28cat1.species%29%3B%20//%20%u52A8%u7269%0A%60%60%60%0A%u8FD9%u6837%u5C31%u662F%u771F%u6B63%u7684%u7EE7%u627F%u4E86%uFF0CCat%u7EE7%u627F%u81EAAnimal%u3002%u8FD9%u91CC%u6709%u4E00%u4E2A%u5C0F%u628A%u620F%uFF1A%60Cat.prototype.constructor%20%3D%20Cat%3B%60%0A%u8FD9%u91CC%u7275%u6D89%u5230%u4E00%u4E2A%u5F88%u9976%u4EBA%u7684%u5C0F%u95EE%u9898%uFF0C%u4E5F%u662F%u5F88%u591A%u4EBA%u5728%u8BA8%u8BBA%u7684%uFF0C%u4E5F%u662Fjsoo%u8EB2%u4E0D%u5F00%u7684%u95EE%u9898%uFF0C%u5C31%u662F%uFF1A%0A%3Econstructor%u7684%u4F5C%u7528%u5230%u5E95%u662F%u4EC0%u4E48%uFF1F%0A%u9996%u5148%uFF0C%u5728js%u4E2D%uFF0C%u6240%u6709%u7684%u7269%u4EF6%u7686%u4E3A%u5BF9%u8C61%u3002%0A%u5176%u6B21%u6BCF%u4E00%u4E2A%u5BF9%u8C61%u90FD%u6709%u4ED6%u7684%u6784%u9020%u51FD%u6570%uFF0C%u7531%60object.constructor%60%u6307%u660E%u3002%0A%u6700%u540E%uFF0C%u6240%u6709%60object.constructor%60%u5747%u7B49%u4E8E%60object.prototype.constructor%60%u3002%u770B%u4E0B%u9762%u7684REPL%u7684%u4EE3%u7801%uFF1A%0A%60%60%60javascript%0A%3E%20function%20Cat%28%29%7B%7D%0A%3E%20function%20Animal%28%29%7B%7D%0A%3E%20Cat.prototype%3Dnew%20Animal%0A%3E%20cat%20%3D%20new%20Cat%28%29%0AAnimal%20%7B%7D%20//%20%u770B%u8FD9%u91CC%u7684%u7ED3%u679C%0A%3E%20cat.constructor%3D%3D%3DAnimal%20//%20%u518D%u770B%u8FD9%u4E00%u884C%0Atrue%0A%3E%20Cat.constructor%20%3D%3D%3D%20Function%0Atrue%0A%60%60%60%0A%u53EF%u89C1%uFF0C%u5982%u679C%u4FEE%u6539%u4E86%u7C7B%u7684prototype%uFF0C%u5B83%u81EA%u5DF1%u5BF9%u8C61%u7684constructor%u5C31%u4F1A%u6307%u5411%u5176%u4ED6%u7684%u5730%u65B9%u3002%u95EE%u9898%u6765%u4E86%uFF1A%0A%3E%u8FD9%u4E48%u505A%u6709%u4EC0%u4E48%u95EE%u9898%u5462%uFF1F%0A%0A%u8FD9%u8BA9%u6211%u767E%u601D%u4E0D%u5F97%u5176%u89E3%u3002%u5F15%u7528%u77E5%u4E4E%u4E0A%u4E00%u4E2A%u56DE%u7B54%uFF1A%0A%3Econstructor%u5C5E%u6027%u4E0D%u5F71%u54CD%u4EFB%u4F55JavaScript%u7684%u5185%u90E8%u5C5E%u6027%u3002instanceof%u68C0%u6D4B%u5BF9%u8C61%u7684%u539F%u578B%u94FE%uFF0C%u901A%u5E38%u4F60%u662F%u65E0%u6CD5%u4FEE%u6539%u7684%uFF08%u4E0D%u8FC7%u67D0%u4E9B%u5F15%u64CE%u901A%u8FC7%u79C1%u6709%u7684__proto__%u5C5E%u6027%u66B4%u9732%u51FA%u6765%uFF09%u3002%0A%3E**constructor%u5176%u5B9E%u6CA1%u6709%u4EC0%u4E48%u7528%u5904%uFF0C%u53EA%u662FJavaScript%u8BED%u8A00%u8BBE%u8BA1%u7684%u5386%u53F2%u9057%u7559%u7269**%u3002%u7531%u4E8Econstructor%u5C5E%u6027%u662F%u53EF%u4EE5%u53D8%u66F4%u7684%uFF0C%u6240%u4EE5%u672A%u5FC5%u771F%u7684%u6307%u5411%u5BF9%u8C61%u7684%u6784%u9020%u51FD%u6570%uFF0C%u53EA%u662F%u4E00%u4E2A%u63D0%u793A%u3002**%u4E0D%u8FC7%uFF0C%u4ECE%u7F16%u7A0B%u4E60%u60EF%u4E0A%uFF0C%u6211%u4EEC%u5E94%u8BE5%u5C3D%u91CF%u8BA9%u5BF9%u8C61%u7684constructor%u6307%u5411%u5176%u6784%u9020%u51FD%u6570%uFF0C%u4EE5%u7EF4%u6301%u8FD9%u4E2A%u60EF%u4F8B**%u3002%0A%0A%u7528instanceof%u770B%u4E0D%u51FA%u4EC0%u4E48%u5DEE%u522B%uFF0C%u53EF%u80FD%u4E5F%u5370%u8BC1%u4E86%u8FD9%u4F4D%u77E5%u4E4E%u5927%u725B%u7684%u89C2%u70B9%u3002%0A%60%60%60javascript%0A%3E%20cat%20%3D%20new%20Cat%28%29%0AAnimal%20%7B%7D%0A%3E%20cat%20instanceof%20Cat%0Atrue%0A%3E%20cat%20instanceof%20Animal%0Atrue%0A%3E%20Cat.prototype.constructor%3DCat%0A%5BFunction%3A%20Cat%5D%0A%3E%20cat%3Dnew%20Cat%28%29%0ACat%20%7B%7D%0A%3E%20cat%20instanceof%20Cat%0Atrue%0A%3E%20cat%20instanceof%20Animal%0Atrue%0A%60%60%60%0A%0A%u518D%u56DE%u5230%u6211%u4EEC%u4E4B%u524D%u7684%u4F8B%u5B50%60Cat.prototype%20%3D%20new%20Animal%28%29%3B%60%u8FD9%u4E00%u53E5%u4E5F%u9690%u542B%u7684%u505A%u4E86%60Cat.prototype.constructor%20%3D%20Animal%60%u3002%u5BFC%u81F4%60cat.constructor%20%3D%3D%3D%20Cat.prototype.constructor%60%0A%u770B%u8D77%u6765%u6709%u70B9%u4E71%uFF0Ccat%u770B%u8D77%u6765%u662F%u7531Animal%u6765%u6784%u9020%u7684%u4E86%u3002%0A%u6211%u7684%u7406%u89E3%u662F%uFF1A%0A%3E%u4E00%u822C%u4F7F%u7528%u4E0D%u4F1A%u6709%u95EE%u9898%uFF0C%u5F53%u5728%u4E00%u4E9B%u9AD8%u7EA7%u5E94%u7528%u573A%u5408%uFF0C%u8981%u7528%u5230%u53CD%u5C04%u7684%u65F6%u5019%uFF0C%u5982%u679Cconstructor%u8BBE%u7F6E%u4E0D%u5BF9%u4F1A%u5F15%u8D77%u4E0D%u5FC5%u8981%u7684%u9EBB%u70E6%u3002%0A%0A%u6839%u636Eprototype%u7EE7%u627F%u7684%u65B9%u6CD5%uFF0C%5BJavascript%u9762%u5411%u5BF9%u8C61%u7F16%u7A0B%uFF08%u4E8C%uFF09%uFF1A%u6784%u9020%u51FD%u6570%u7684%u7EE7%u627F%5D%28https%3A//www.evernote.com/shard/s24/nl/697376807/9f56f8ec-b75a-469d-9320-d3d206766dc1/%29%u53C8%u63D0%u51FA%u4E86%u4E24%u79CD%u6539%u8FDB%u578B%u7684%u7EE7%u627F%u65B9%u6CD5%uFF0C%u5206%u522B%u662F%0A-%20%u76F4%u63A5%u7EE7%u627Fprototype%0A-%20%u5229%u7528%u7A7A%u5BF9%u8C61%u4F5C%u4E3A%u4E2D%u4ECB%0A%0A%23%23%23%23%u76F4%u63A5%u7EE7%u627Fprototype%0A%u7C7B%u7684prototype%u662F%u8FD9%u4E2A%u7C7B%u7A33%u5B9A%u7684%u90E8%u5206%uFF0C%u5F53%u6211%u4EEC%u60F3%u7EE7%u627F%u7684%u65F6%u5019%uFF0C%u5176%u5B9E%u5C31%u662F%u60F3%u7EE7%u627F%u7C7B%u4E2D%u7A33%u5B9A%u7684%u90E8%u5206%u3002%u6240%u4EE5%u8FD9%u4E2A%u65B9%u6CD5%u5C31%u662F%u76F4%u63A5%u7EE7%u627F%u7236%u7C7B%u7684prototype%uFF1A%0A%60%60%60javascript%0Afunction%20Animal%28%29%7B%0A%09this.species%20%3D%20%22%u52A8%u7269%22%3B%0A%7D%0ACat.prototype%20%3D%20Aninal.prototype%3B%0ACat.prototype.constructor%20%3D%20Cat%3B%0A%60%60%60%0A%u867D%u7136%u542C%u8D77%u6765%u6709%u7406%uFF0C%u4E0D%u8FC7%u771F%u7684%u8FD9%u4E48%u505A%u8FD8%u662F%u633A%u5947%u602A%u7684%u3002%u5B50%u7C7B%u548C%u7236%u7C7B%u7684prototype%u6307%u5411%u4E86%u540C%u4E00%u4E2A**%u5BF9%u8C61**%28JSOO%u4E2D%uFF0C%u4E07%u7269%u7686%u5BF9%u8C61%uFF0C%u548CC++/Java%u4E2D%u7684%u5BF9%u8C61%u6982%u5FF5%u6709%u7A0D%u8BB8%u533A%u522B%29%u3002%0A%u800C%u5BF9Cat.prototype.constructor%u7684%u4FEE%u6539%u76F4%u63A5%u5F71%u54CD%u4E86Animal.constructor%u3002%0A%0A%u7ED3%u8BBA%u5C31%u662F%u8FD9%u4E2A%u65B9%u6CD5%u4E0D%u597D%uFF0C%u4E0D%u80FD%u7528%u3002%0A%0A%23%23%23%23%u5229%u7528%u7A7A%u5BF9%u8C61%u4F5C%u4E3A%u4E2D%u4ECB%0A%u76F4%u63A5%u4E0A%u4EE3%u7801%uFF1A%0A%60%60%60javascript%0Avar%20F%20%3D%20function%28%29%7B%7D%3B%0AF.prototype%20%3D%20Animal.prototype%3B%0ACat.prototype%20%3D%20new%20F%28%29%3B%0ACat.prototype.constructor%20%3D%20Cat%3B%0A%60%60%60%0A%u8FD9%u6837%u7684%u505A%u6CD5%u9694%u79BB%u4E86%u7236%u7C7B%u548C%u5B50%u7C7B%u3002%0A-%20%u4F18%u70B9%u662F%u8282%u7701%u5185%u5B58%0A%09-%20%u56E0%u4E3AF%u662F%u7A7A%u51FD%u6570%uFF0C%u5B9A%u4E49Cat.prototype%u7684%u65F6%u5019%uFF0Cnew%u7684%u662FF%u6BD4new%u4E00%u4E2AAnimal%u80AF%u5B9A%u662F%u8981%u8282%u7701%u5185%u5B58%u7684%u3002%0A-%20%u7F3A%u70B9%u662F%u4E0D%u592A%u76F4%u89C2%u3002%0A%0A%u4E0D%u8FC7%u636E%u8BF4%u6709%u4E9BJS%u5E93%uFF08YUI%uFF09%u7684OO%u5C31%u662F%u7528%u7684%u8FD9%u79CD%u6A21%u5F0F%u3002%0A%0A%u4F7F%u7528%u65B9%u6CD5%uFF1A%0A%60%60%60javascript%0Afunction%20extend%28Child%2C%20Parent%29%20%7B%0A%09var%20F%20%3D%20function%28%29%7B%7D%3B%0A%09F.prototype%20%3D%20Parent.prototype%3B%0A%09Child.prototype%20%3D%20new%20F%28%29%3B%0A%09Child.prototype.constructor%20%3D%20Child%3B%0A%09Child.uber%20%3D%20Parent.prototype%3B%0A%7D%0A%0Aextend%28Cat%2CAnimal%29%3B%0Avar%20cat1%20%3D%20new%20Cat%28%22%u5927%u6BDB%22%2C%22%u9EC4%u8272%22%29%3B%0Aalert%28cat1.species%29%3B%20//%20%u52A8%u7269%0A%60%60%60%0A%0A%u8FD9%u91CC%u7528%u5230%u4E86%u4E00%u4E2A%u7C7B%u7684uber%u5C5E%u6027%u3002%u8FD9%u662F%u4EC0%u4E48%uFF1F%0A%3E%u610F%u601D%u662F%u4E3A%u5B50%u5BF9%u8C61%u8BBE%u4E00%u4E2Auber%u5C5E%u6027%uFF0C%u8FD9%u4E2A%u5C5E%u6027%u76F4%u63A5%u6307%u5411%u7236%u5BF9%u8C61%u7684prototype%u5C5E%u6027%u3002%uFF08uber%u662F%u4E00%u4E2A%u5FB7%u8BED%u8BCD%uFF0C%u610F%u601D%u662F%22%u5411%u4E0A%22%u3001%22%u4E0A%u4E00%u5C42%22%u3002%uFF09%u8FD9%u7B49%u4E8E%u5728%u5B50%u5BF9%u8C61%u4E0A%u6253%u5F00%u4E00%u6761%u901A%u9053%uFF0C%u53EF%u4EE5%u76F4%u63A5%u8C03%u7528%u7236%u5BF9%u8C61%u7684%u65B9%u6CD5%u3002%u8FD9%u4E00%u884C%u653E%u5728%u8FD9%u91CC%uFF0C%u53EA%u662F%u4E3A%u4E86%u5B9E%u73B0%u7EE7%u627F%u7684%u5B8C%u5907%u6027%uFF0C%u7EAF%u5C5E%u5907%u7528%u6027%u8D28%u3002%0A%0A%u6211%u7684%u7406%u89E3%uFF0C%u7ED9%u5B50%u7C7B%u80FD%u8BBF%u95EE%u7236%u7C7B%u51FD%u6570%u7684%u673A%u4F1A%u3002%u4E3A%u4EC0%u4E48%u53EBuber%uFF0C%u6211%u89C9%u5F97%u5E94%u8BE5%u662F%u7EA6%u5B9A%u4FD7%u6210%u7684%u3002%0A%0A%60%60%60javascript%0A%3E%20function%20Cat2%28%29%7B%7D%0A%3E%20%27uber%27%20in%20Cat2%0Afalse%0A%3E%0A%60%60%60%0A%0A%u6700%u540E%u8FD8%u6709%u4E00%u4E2A%u53E6%u7C7B%uFF0C%u901A%u8FC7%u5728%u6784%u9020%u51FD%u6570%u91CC%u9762%u62F7%u8D1D%u7236%u7C7B%u7684%u5C5E%u6027%u8FBE%u5230%u7EE7%u627F%u7684%u6548%u679C%uFF0C%u8FD9%u4E2A%u548C%u540E%u4E00%u7AE0%u7684%u7EE7%u627F%u65B9%u6CD5%u6709%u5F02%u66F2%u540C%u5DE5%u4E4B%u6548%u3002%u533A%u522B%u662F%u8FD9%u91CC%u662F%u5728%u6784%u9020%u51FD%u6570%u91CC%u505A%u62F7%u8D1D%u3002%0A%60%60%60javascript%0Afunction%20extend2%28Child%2C%20Parent%29%20%7B%0A%09var%20p%20%3D%20Parent.prototype%3B%0A%09var%20c%20%3D%20Child.prototype%3B%0A%09for%20%28var%20i%20in%20p%29%20%7B%0A%09%09c%5Bi%5D%20%3D%20p%5Bi%5D%3B%0A%09%7D%0A%09c.uber%20%3D%20p%3B%0A%7D%0A%0Aextend2%28Cat%2C%20Animal%29%3B%0Avar%20cat1%20%3D%20new%20Cat%28%22%u5927%u6BDB%22%2C%22%u9EC4%u8272%22%29%3B%0Aalert%28cat1.species%29%3B%20//%20%u52A8%u7269%0A%60%60%60%0A%u6CE8%u610F%5BJavascript%u9762%u5411%u5BF9%u8C61%u7F16%u7A0B%uFF08%u4E8C%uFF09%uFF1A%u6784%u9020%u51FD%u6570%u7684%u7EE7%u627F%5D%28https%3A//www.evernote.com/shard/s24/nl/697376807/9f56f8ec-b75a-469d-9320-d3d206766dc1/%29%u4E2D%u867D%u7136%u6CA1%u6709%u63D0%u5230%uFF0C%u4F46%u8FD9%u91CC%u4ECD%u7136%u4F1A%u6709%u6DF1%u62F7%u8D1D%uFF0C%u6D45%u62F7%u8D1D%u7684%u95EE%u9898%u3002%0A%23%23%23%u901A%u8FC7%u62F7%u8D1D%u7EE7%u627F%0A%u8FD9%u91CC%u89E3%u51B3%u7684%u95EE%u9898%uFF0C%u548C%u6807%u51C6%u7684OO%u5904%u7406%u7684%u95EE%u9898%u4E0D%u540C%u3002%u8FD9%u91CC%u8981%u5904%u7406%u7684%u95EE%u9898%uFF0C%u662F%u8BA9%u4E24%u4E2A%u666E%u901A%u5BF9%u8C61%u4E4B%u95F4%u6709%u7EE7%u627F%u5173%u7CFB%uFF1A%0A%60%60%60javascript%0Avar%20Chinese%20%3D%20%7B%0A%09nation%3A%27%u4E2D%u56FD%27%0A%7D%3B%0Avar%20Doctor%20%3D%7B%0A%09career%3A%27%u533B%u751F%27%0A%7D%0A%60%60%60%0A%u6CE8%u610F%u8FD9%u91CC%u90FD%u662F%u666E%u901A%u7684%u5B57%u5178%u7C7B%u578B%u5BF9%u8C61%uFF0C%u5E76%u4E0D%u50CF%u524D%u4E00%u7AE0%u8BF4%u7684%u662F%u7528function%u6784%u9020%u51FA%u6765%u7684%u7C7B%uFF0C%u518D%u7528new%u6784%u9020%u7684%u5BF9%u8C61%u3002%0A%u73B0%u5728%u8981%u89E3%u51B3%u7684%u95EE%u9898%uFF0C%u662F%27%u4E2D%u56FD%u533B%u751F%27%u8981%u5982%u4F55%u5B9A%u4E49%u3002%u8FD9%u91CC%u5F88%u5BB9%u6613%u60F3%u5230%u5C31%u662F%u628AChinese%u91CC%u9762%u7684%u5C5E%u6027%u62F7%u7ED9Doctor%u5C31%u597D%u4E86%u561B%u3002%u90A3%u8FD9%u4E00%u7AE0%u8BB2%u7684%u5C31%u662F%u8FD9%u4E2A%u5185%u5BB9%u3002%0A%0A%23%23%23%23object%28%29%u65B9%u6CD5%0A%u8FD9%u4E2A%u65B9%u6CD5%u5176%u5B9E%u8FD8%u662F%u5728%u7528%u6784%u9020%u51FD%u6570%u3002%0A%60%60%60javascript%0Afunction%20object%28o%29%20%7B%0A%09function%20F%28%29%20%7B%7D%0A%09F.prototype%20%3D%20o%3B%0A%09return%20new%20F%28%29%3B%0A%7D%0A%0Avar%20Doctor%20%3D%20object%28Chinese%29%3B%0ADoctor.career%20%3D%20%27%u533B%u751F%27%3B%0Aalert%28Doctor.nation%29%3B%20//%u4E2D%u56FD%0A%60%60%60%0A%u6839%u636E%u4E0A%u4E00%u7AE0%u6211%u4EEC%u8BB2%u7684%u6784%u9020%u51FD%u6570%u7684%u77E5%u8BC6%uFF0C%u53EF%u4EE5%u77E5%u9053%uFF1A%0A-%20Doctor%u662FF%u7C7B%u7684%u5BF9%u8C61%0A-%20F%u7C7B%u901A%u8FC7prototype%u7EE7%u627F%u4E86Chinese%u5BF9%u8C61%0A-%20%u6709%u4E0B%u9762%u7684%u7B49%u5F0F%0A%60%60%60javascript%0A%3E%20Doctor.constructor%20%3D%3D%3D%20Chinese.constructor%0Atrue%0A%60%60%60%0ADoctor%u662FF%u7C7B%u7684%u5BF9%u8C61%uFF0CF%u7C7B%u7684prototype%u662FChinese%u5BF9%u8C61%uFF0CDoctor%u7684constructor%u6307%u5411%u4ED6%u539F%u578B%u7684contructor%u3002%0A%u975E%u8981%u548C%u6784%u9020%u51FD%u6570%u626F%u4E0A%u5173%u7CFB%uFF0C%u662F%u60F3%u5BF9%u4E0A%u4E00%u7AE0%u7684%u77E5%u8BC6%u6D3B%u5B66%u6D3B%u7528%u3002%u4F5C%u8005%u628A%u8FD9%u4E2A%u65B9%u6CD5%u5217%u5728%u8FD9%u91CC%u7684%u7528%u610F%uFF0C%u5176%u5B9E%u662F%u8FD9%u91CC%u7684%u65B9%u6CD5%u8DDF%u6784%u9020%u51FD%u6570%uFF0Cconstructor%u6CA1%u534A%u6BDB%u94B1%u5173%u7CFB%u3002**%u53EA%u662F%u5355%u7EAF%u7684%u5C06Chinese%u901A%u8FC7prototype%u4F20%u7ED9Doctor**%u3002%0A%0A%u540E%u9762%u7684%u65B9%u6CD5%u5C31%u4ECE%u5F62%u5F0F%u4E0A%u4E5F%u548C%u6784%u9020%u51FD%u6570%u4E5F%u6CA1%u534A%u6BDB%u94B1%u5173%u7CFB%u4E86%u3002%0A-%20%u6D45%u62F7%u8D1D%0A-%20%u6DF1%u62F7%u8D1D%0A%u4E0D%u8BB2%u6D45%u62F7%u8D1D%uFF0C%u56E0%u4E3A%u6DF1%u62F7%u8D1D%u5305%u542B%u4E86%u6D45%u62F7%u8D1D%0A%0A%23%23%23%23%u6DF1%u62F7%u8D1D%0A%u5176%u5B9E%u62F7%u8D1D%u5BF9%u4E8E%u89E3%u51B3%u8FD9%u4E2A%u95EE%u9898%uFF0C%u672C%u8EAB%u5C31%u662F%u6700%u76F4%u89C9%u7684%u65B9%u6848%u3002%u76F4%u63A5%u770Bcode%uFF1A%0A%60%60%60javascript%0Afunction%20deepCopy%28p%2C%20c%29%20%7B%0A%09var%20c%20%3D%20c%20%7C%7C%20%7B%7D%3B%0A%09for%20%28var%20i%20in%20p%29%20%7B%0A%09%09if%20%28typeof%20p%5Bi%5D%20%3D%3D%3D%20%27object%27%29%20%7B%0A%09%09%09c%5Bi%5D%20%3D%20%28p%5Bi%5D.constructor%20%3D%3D%3D%20Array%29%20%3F%20%5B%5D%20%3A%20%7B%7D%3B%0A%09%09%09deepCopy%28p%5Bi%5D%2C%20c%5Bi%5D%29%3B%0A%09%09%7D%20else%20%7B%0A%09%09%09c%5Bi%5D%20%3D%20p%5Bi%5D%3B%0A%09%09%7D%0A%09%7D%0A%09return%20c%3B%0A%7D%0Avar%20Doctor%20%3D%20deepCopy%28Chinese%29%3B%0A%60%60%60%0A%u8981%u7528%u6DF1%u62F7%u8D1D%u7684%u539F%u56E0%u662FChinese%u5BF9%u8C61%u5185%u90E8%u53EF%u80FD%u6709%u6210%u5458%u7684%u503C%u662F%u6570%u7EC4%u6216%u5176%u4ED6%u5BF9%u8C61%u3002jQuery%u5E93%u7528%u7684%u5C31%u662F%u8FD9%u79CD%u7EE7%u627F%u65B9%u6CD5%u3002%0A%0A

Edit

最近拜读了Kent Beck大神的《Test-Driven Development By Example》,有了这篇读书笔记。这本书解答了我的很多开发中遇到的困惑,所以决定以后走上TDD的道路。那为什么会学习UnixTest++,其实是因为惠普打印机的Phoenix FW里面曾经有先辈做过类似unit test的事情,称之为Linux Test Harness。Harnes是马鞍的意思,这个名字应该是惠普人自己取的。这个Linux Test Harness就是基于UnixTest++的。目前我用的是最新版,v1.5.1。目录树结构如下:
├── AUTHORS
├── CMakeLists.txt
├── ChangeLog
├── INSTALL
├── LICENSE
├── Makefile.am
├── README.md
├── UnitTest++
├── UnitTest++.pc.in
├── appveyor.yml
├── builds
├── configure.ac
├── docs
├── m4
│ ├── ax_cxx_compile_stdcxx.m4
│ └── ax_cxx_compile_stdcxx_11.m4
└── tests

7 directories, 95 files
重要的文件或文件夹我标成了红色。

  • CMakeLists.txt
    • UnitTest++是基于CMake的,后面会具体讲CMake,这个文件就类似于CMake的工程文件。CMake基于它生成Makefile或者其他开发平台的工程文件。
  • UnitTest++
    • UnitTest++是开源项目,这个文件夹就是UnitTest++所有的源码
  • tests
    • 这个是UnitTest++本身的test case。我们在写自己的test case的时候完全可以参考别人是怎么写的。

CMake

这里收集了两篇CMake的文章,CMake使用总结CMake 入门实战
CMake意为Cross-platform Make,可用于管理C/C++工程。你或许听过好几种 Make 工具,例如 GNU Make ,QT 的 qmake ,微软的 MS nmake,BSD Make(pmake),Makepp,等等。这些 Make 工具遵循着不同的规范和标准,所执行的 Makefile 格式也千差万别。这样就带来了一个严峻的问题:如果软件想跨平台,必须要保证能够在不同平台编译。而如果使用上面的 Make 工具,就得为每一种标准写一次 Makefile ,这将是一件让人抓狂的工作。就是针对这个问题所设计的工具。它首先允许开发者编写一种平台无关的 CMakeList.txt 文件来定制整个编译流程,然后再根据目标用户的平台进一步生成所需的本地化 Makefile 和工程文件,如 Unix 的 Makefile 或 Windows 的 Visual Studio 工程。从而做到“Write once, run everywhere”。
在 linux 平台下使用 CMake 生成 Makefile 并编译的流程如下:

  1. 编写 CMake 配置文件 CMakeLists.txt
  2. 执行命令cmake -G "Unix Makefiles" <PATH>生成Unix识别的Makefile[^support],<PATH>是 CMakeLists.txt 所在的目录。也可以用ccmake,ccmake 和 cmake 的区别在于前者提供了一个交互式的界面。
  3. 使用 make 命令进行编译。
    以UnitTest++自带的CMakeLists.txt为例:
cmake_minimum_required(VERSION 2.8.1)
project(UnitTest++)

# get the main sources
file(GLOB headers_ RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} UnitTest++/*.h)
file(GLOB sources_ RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} UnitTest++/*.cpp)
source_group("" FILES ${headers_} ${sources_})

# get platform specific sources
if (WIN32)
add_definitions(-D_CRT_SECURE_NO_DEPRECATE)
set(platformDir_ Win32)
else()
set(platformDir_ Posix)
endif(WIN32)

file(GLOB platformHeaders_ RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} UnitTest++/${platformDir_}/*.h)
file(GLOB platformSources_ RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} UnitTest++/${platformDir_}/*.cpp)
source_group(${platformDir_} FILES ${platformHeaders_} ${platformSources_})

# create the lib
add_library(UnitTest++ STATIC ${headers_} ${sources_} ${platformHeaders_} ${platformSources_})

# build the test runner
file(GLOB TEST_SRCS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} tests/*.cpp tests/*.h)
source_group( "" FILES ${TEST_SRCS})
add_executable(TestUnitTest++ ${TEST_SRCS})
include_directories(.)

if(${UTPP_USE_PLUS_SIGN})
set_target_properties(TestUnitTest++ PROPERTIES OUTPUT_NAME TestUnitTest++)
endif()

target_link_libraries(TestUnitTest++ UnitTest++)

# run unit tests as post build step
add_custom_command(TARGET TestUnitTest++
POST_BUILD COMMAND TestUnitTest++
COMMENT "Running unit tests")

set, file, add_library等等都是命令,具体命令可以参考这个笔记CMake使用总结

  • add_library(UnitTest++…)将UnitTest++代码编译成静态库
  • add_executable(TestUnitTest++ …)生成UnitTest++应用文件
  • target_link_libraries(TestUnitTest++ UnitTest++)将前面生成的libUnitTest++.a链入TestUnitTest++可执行文件。
  • add_custom_command(TARGET TestUnitTest++…)这一行决定了运行make时,额外调用的命令。

CMake使用总结这篇笔记里提到:

按照官方文档的说法,不建议使用file的GLOB指令来收集工程的源文件,原文解释如下
We do not recommend using GLOB to collect a list of source files from your source tree. If no CMakeLists.txt file changes when a source is added or removed then the generated build system cannot know when to ask CMake to regenerate.
大意就是,GLOB收集的源文件增加或删除,而CMakeLists.txt没有发生修改时,CMake不能识别这些文件。其实,当CMakeLists.txt使用aux_source_directory和file glob查找工程源文件时,如果添加或删除源文件,都需要重新运行CMake

cmake 如何编出可以GDB的程序?

cmake -DCMAKE_BUILD_TYPE=Debug CMakeList.txt

Implement Test Cases

Test Case的编写通过UnixTest++本身的宏引入:

    SUITE(MyUnitTests)
{
TEST(StarterTest)
{
MonoGraphCpInit(false);
CHECK(true);
}
}

运行make之后的输出为:

Running unit tests
Success: 2 tests passed.
Test time: 7.54 seconds.
[100%] Built target MyTest

TEST宏引入的就是一个test case,SUITE宏引入的是一个测试套件。默认情况下,所有用SUITE或者TEST定义的case都会被运行,看main code。

int main(int, const char *[])
{
return UnitTest::RunAllTests();
}

因为我们有UnitTest++的源码,其实想怎么测都可以。经过学习,如果想调用指定case的main code可以如下编写:

   UnitTest::TestReporterStdout reporter;
UnitTest::TestRunner runner(reporter);
return runner.RunTestsIf(UnitTest::Test::GetTestList(), "MyUnitTests", UnitTest::True(), 0);

这段code调用了名字叫MyUnitTests的SUITE中所有case。

Linux Test Harness

Phoenix FW里面的Linux Test Harness是基于UnixTest++的。可以参考components/GCP/JsonDataModel/linux_test_harness/Makefile。其中用到的UnixTest++是v1.4的,并没有用到cmake来构建。而是自己构建的Makefile来编译。运用最新的cmake来搭建Unit Test环境感觉更简单,而且竟然还能有平台移植性,虽然目前对我们没用,不过还是很赞的。将UnixTest++编译到可用还是挺简单的,但是要整合Phoenix code有那么一点麻烦。先讲一下我们的使用场景,测试Mono Graphic Panel Driver。再将具体搭建的思路和一些小tips。

Mono Graphic Panel Driver

这里有一篇相关笔记Pheonix_Display。我其实想做的是在Mono Graphic Panel Driver中添加一个Recovery Thread。先看下源码架构:

[benzhou@plslx111 phx]$ ls components/bsp/control_panel/
abc buttonLed ccpi clcd inc monoGraph obj pub src

monoGraph中的代码会调用到abc文件夹下的代码。所以linux_test_harness就放在这个文件夹下。并且abcmonoGraph中的代码需要被链接到我们的测试程序中。

测试架构

最后我们的CMakeLists.txt如下:

cmake_minimum_required(VERSION 2.8.1)
project(MyTest)

set(CMAKE_C_COMPILER "g++")

# enable gdb
set(CMAKE_BUILD_TYPE "Debug")
set(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb")
set(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall")

set(PHX_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../../../")

include_directories(..)
include_directories(stubs)
include_directories(${PHX_DIR}components/bsp/control_panel/monoGraph/pub/)
include_directories(${PHX_DIR}components/bsp/control_panel/monoGraph/inc/)
include_directories(${PHX_DIR}components/bsp/control_panel/abc/pub/)
include_directories(${PHX_DIR}components/bsp/control_panel/abc/inc/)
include_directories(${PHX_DIR}components/bsp/control_panel/pub/)
include_directories(${PHX_DIR}components/bsp/control_panel/inc/)
include_directories(${PHX_DIR}components/primboot/pub/)

# get Phoenix code
#file(GLOB PHX_SRCS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ../../../src/*.cpp ../../../src/*.c)
aux_source_directory(${PHX_DIR}/components/bsp/control_panel/abc/src PHX_ABC)
aux_source_directory(${PHX_DIR}/components/bsp/control_panel/monoGraph/src PHX_MONO)
list(APPEND PHX_SRCS
${PHX_ABC}
${PHX_MONO}
)
add_library (PhxFunctions STATIC ${PHX_SRCS})

# get the Phoenix stub sources
aux_source_directory(${PHX_DIR}/components/bsp/control_panel/linux_test_harness/mytests/stubs PHX_STUBS)
add_library (StubFunctions STATIC ${PHX_STUBS})

# get the main sources
aux_source_directory(${PHX_DIR}/components/bsp/control_panel/linux_test_harness/mytests TEST_SRCS)

add_executable(MyTest ${TEST_SRCS})

target_link_libraries(MyTest
pthread
UnitTest++
PhxFunctions
StubFunctions
)

# run unit tests as post build step
add_custom_command(TARGET MyTest
POST_BUILD COMMAND MyTest
COMMENT "Running unit tests"
)

除了UnitTest++框架本身的库libUnitTest++.a和链入了一个pthread库以外,我们还定义了另外两个库PhxFunctions和StubFunctions。

  • PhxFunctions是我们想测试的code,即abcmonoGraph文件夹
  • StubFunctions是为了编译通过,定义的一些存根函数

主要需要注意的是怎么定义这些存根文件和函数。根据我的经验和TDD思想(不要为了明天设计),应该注意:

  • 如果有找不到的头文件,在stubs文件夹里加一个空文件
  • 如果有找不到的类声明,从FW找到类定义拷贝过来
  • 如果有找不到定义的函数的链接错误,就在stub.cpp中定义空函数。
  • 如果将来发现需要调用真实的FW函数,就需要将那个函数所在的源码文件编译成库链接进来。
%23UnitTest++%u7B80%u4ECB%0A@%28%u5B66%u4E60%u7B14%u8BB0%29%5BTDD%2C%20Programming%2C%20unittest%5D%0A%0A%5BTOC%5D%0A%0A%u6700%u8FD1%u62DC%u8BFB%u4E86Kent%20Beck%u5927%u795E%u7684%u300ATest-Driven%20Development%20By%20Example%u300B%uFF0C%u6709%u4E86%u8FD9%u7BC7%5B%u8BFB%u4E66%u7B14%u8BB0%5D%28https%3A//www.evernote.com/shard/s24/nl/697376807/209575d9-d1bc-4aa6-8a79-3d50bfe3d377/%29%u3002%u8FD9%u672C%u4E66%u89E3%u7B54%u4E86%u6211%u7684%u5F88%u591A%u5F00%u53D1%u4E2D%u9047%u5230%u7684%u56F0%u60D1%uFF0C%u6240%u4EE5%u51B3%u5B9A%u4EE5%u540E%u8D70%u4E0ATDD%u7684%u9053%u8DEF%u3002%u90A3%u4E3A%u4EC0%u4E48%u4F1A%u5B66%u4E60UnixTest++%uFF0C%u5176%u5B9E%u662F%u56E0%u4E3A%u60E0%u666E%u6253%u5370%u673A%u7684Phoenix%20FW%u91CC%u9762%u66FE%u7ECF%u6709%u5148%u8F88%u505A%u8FC7%u7C7B%u4F3Cunit%20test%u7684%u4E8B%u60C5%uFF0C%u79F0%u4E4B%u4E3ALinux%20Test%20Harness%u3002Harnes%u662F%u9A6C%u978D%u7684%u610F%u601D%uFF0C%u8FD9%u4E2A%u540D%u5B57%u5E94%u8BE5%u662F%u60E0%u666E%u4EBA%u81EA%u5DF1%u53D6%u7684%u3002%u8FD9%u4E2ALinux%20Test%20Harness%u5C31%u662F%u57FA%u4E8E%5BUnixTest++%5D%28https%3A//github.com/unittest-cpp/unittest-cpp%29%u7684%u3002%u76EE%u524D%u6211%u7528%u7684%u662F%u6700%u65B0%u7248%uFF0Cv1.5.1%u3002%u76EE%u5F55%u6811%u7ED3%u6784%u5982%u4E0B%uFF1A%0A%u251C%u2500%u2500%20AUTHORS%0A%u251C%u2500%u2500%20%3Cfont%20color%3D%22IndianRed%22%3ECMakeLists.txt%3C/font%3E%0A%u251C%u2500%u2500%20ChangeLog%0A%u251C%u2500%u2500%20INSTALL%0A%u251C%u2500%u2500%20LICENSE%0A%u251C%u2500%u2500%20Makefile.am%0A%u251C%u2500%u2500%20README.md%0A%u251C%u2500%u2500%20%3Cfont%20color%3D%22IndianRed%22%3EUnitTest++%3C/font%3E%0A%u251C%u2500%u2500%20UnitTest++.pc.in%0A%u251C%u2500%u2500%20appveyor.yml%0A%u251C%u2500%u2500%20builds%0A%u251C%u2500%u2500%20configure.ac%0A%u251C%u2500%u2500%20docs%0A%u251C%u2500%u2500%20m4%0A%u2502%20%20%20%u251C%u2500%u2500%20ax_cxx_compile_stdcxx.m4%0A%u2502%20%20%20%u2514%u2500%u2500%20ax_cxx_compile_stdcxx_11.m4%0A%u2514%u2500%u2500%20%3Cfont%20color%3D%22IndianRed%22%3Etests%3C/font%3E%0A%0A7%20directories%2C%2095%20files%0A%u91CD%u8981%u7684%u6587%u4EF6%u6216%u6587%u4EF6%u5939%u6211%u6807%u6210%u4E86%u7EA2%u8272%u3002%0A-%20CMakeLists.txt%0A%09-%20UnitTest++%u662F%u57FA%u4E8ECMake%u7684%uFF0C%u540E%u9762%u4F1A%u5177%u4F53%u8BB2CMake%uFF0C%u8FD9%u4E2A%u6587%u4EF6%u5C31%u7C7B%u4F3C%u4E8ECMake%u7684%u5DE5%u7A0B%u6587%u4EF6%u3002CMake%u57FA%u4E8E%u5B83%u751F%u6210Makefile%u6216%u8005%u5176%u4ED6%u5F00%u53D1%u5E73%u53F0%u7684%u5DE5%u7A0B%u6587%u4EF6%u3002%0A-%20UnitTest++%0A%09-%20UnitTest++%u662F%u5F00%u6E90%u9879%u76EE%uFF0C%u8FD9%u4E2A%u6587%u4EF6%u5939%u5C31%u662FUnitTest++%u6240%u6709%u7684%u6E90%u7801%0A-%20tests%0A%09-%20%u8FD9%u4E2A%u662FUnitTest++%u672C%u8EAB%u7684test%20case%u3002%u6211%u4EEC%u5728%u5199%u81EA%u5DF1%u7684test%20case%u7684%u65F6%u5019%u5B8C%u5168%u53EF%u4EE5%u53C2%u8003%u522B%u4EBA%u662F%u600E%u4E48%u5199%u7684%u3002%0A%0A%23%23CMake%0A%u8FD9%u91CC%u6536%u96C6%u4E86%u4E24%u7BC7CMake%u7684%u6587%u7AE0%uFF0C%5BCMake%u4F7F%u7528%u603B%u7ED3%5D%28https%3A//www.evernote.com/shard/s24/nl/697376807/d90d734c-12e4-4664-a0d5-af887f4b8b10/%29%u548C%5BCMake%20%u5165%u95E8%u5B9E%u6218%5D%28https%3A//www.evernote.com/shard/s24/nl/697376807/50e6782c-3acc-42a7-b582-43fe4a381fc8/%29%0ACMake%u610F%u4E3ACross-platform%20Make%uFF0C%u53EF%u7528%u4E8E%u7BA1%u7406C/C++%u5DE5%u7A0B%u3002%u4F60%u6216%u8BB8%u542C%u8FC7%u597D%u51E0%u79CD%20Make%20%u5DE5%u5177%uFF0C%u4F8B%u5982%20GNU%20Make%20%uFF0CQT%20%u7684%20qmake%20%uFF0C%u5FAE%u8F6F%u7684%20MS%20nmake%uFF0CBSD%20Make%uFF08pmake%uFF09%uFF0CMakepp%uFF0C%u7B49%u7B49%u3002%u8FD9%u4E9B%20Make%20%u5DE5%u5177%u9075%u5FAA%u7740%u4E0D%u540C%u7684%u89C4%u8303%u548C%u6807%u51C6%uFF0C%u6240%u6267%u884C%u7684%20Makefile%20%u683C%u5F0F%u4E5F%u5343%u5DEE%u4E07%u522B%u3002%u8FD9%u6837%u5C31%u5E26%u6765%u4E86%u4E00%u4E2A%u4E25%u5CFB%u7684%u95EE%u9898%uFF1A%u5982%u679C%u8F6F%u4EF6%u60F3%u8DE8%u5E73%u53F0%uFF0C%u5FC5%u987B%u8981%u4FDD%u8BC1%u80FD%u591F%u5728%u4E0D%u540C%u5E73%u53F0%u7F16%u8BD1%u3002%u800C%u5982%u679C%u4F7F%u7528%u4E0A%u9762%u7684%20Make%20%u5DE5%u5177%uFF0C%u5C31%u5F97%u4E3A%u6BCF%u4E00%u79CD%u6807%u51C6%u5199%u4E00%u6B21%20Makefile%20%uFF0C%u8FD9%u5C06%u662F%u4E00%u4EF6%u8BA9%u4EBA%u6293%u72C2%u7684%u5DE5%u4F5C%u3002%u5C31%u662F%u9488%u5BF9%u8FD9%u4E2A%u95EE%u9898%u6240%u8BBE%u8BA1%u7684%u5DE5%u5177%u3002%u5B83%u9996%u5148%u5141%u8BB8%u5F00%u53D1%u8005%u7F16%u5199%u4E00%u79CD%u5E73%u53F0%u65E0%u5173%u7684%20CMakeList.txt%20%u6587%u4EF6%u6765%u5B9A%u5236%u6574%u4E2A%u7F16%u8BD1%u6D41%u7A0B%uFF0C%u7136%u540E%u518D%u6839%u636E%u76EE%u6807%u7528%u6237%u7684%u5E73%u53F0%u8FDB%u4E00%u6B65%u751F%u6210%u6240%u9700%u7684%u672C%u5730%u5316%20Makefile%20%u548C%u5DE5%u7A0B%u6587%u4EF6%uFF0C%u5982%20Unix%20%u7684%20Makefile%20%u6216%20Windows%20%u7684%20Visual%20Studio%20%u5DE5%u7A0B%u3002%u4ECE%u800C%u505A%u5230%u201CWrite%20once%2C%20run%20everywhere%u201D%u3002%0A%09%u5728%20linux%20%u5E73%u53F0%u4E0B%u4F7F%u7528%20CMake%20%u751F%u6210%20Makefile%20%u5E76%u7F16%u8BD1%u7684%u6D41%u7A0B%u5982%u4E0B%uFF1A%0A1.%20%u7F16%u5199%20CMake%20%u914D%u7F6E%u6587%u4EF6%20%60CMakeLists.txt%60%20%u3002%0A2.%20%u6267%u884C%u547D%u4EE4%60cmake%20-G%20%22Unix%20Makefiles%22%20%3CPATH%3E%60%u751F%u6210Unix%u8BC6%u522B%u7684Makefile%5B%5Esupport%5D%uFF0C%26lt%3BPATH%26gt%3B%u662F%20CMakeLists.txt%20%u6240%u5728%u7684%u76EE%u5F55%u3002%u4E5F%u53EF%u4EE5%u7528ccmake%uFF0Cccmake%20%u548C%20cmake%20%u7684%u533A%u522B%u5728%u4E8E%u524D%u8005%u63D0%u4F9B%u4E86%u4E00%u4E2A%u4EA4%u4E92%u5F0F%u7684%u754C%u9762%u3002%0A3.%20%u4F7F%u7528%20make%20%u547D%u4EE4%u8FDB%u884C%u7F16%u8BD1%u3002%0A%u4EE5UnitTest++%u81EA%u5E26%u7684CMakeLists.txt%u4E3A%u4F8B%uFF1A%0A%60%60%60cmake%0Acmake_minimum_required%28VERSION%202.8.1%29%0Aproject%28UnitTest++%29%0A%0A%23%20get%20the%20main%20sources%0Afile%28GLOB%20headers_%20RELATIVE%20%24%7BCMAKE_CURRENT_SOURCE_DIR%7D%20UnitTest++/*.h%29%0Afile%28GLOB%20sources_%20RELATIVE%20%24%7BCMAKE_CURRENT_SOURCE_DIR%7D%20UnitTest++/*.cpp%29%0Asource_group%28%22%22%20FILES%20%24%7Bheaders_%7D%20%24%7Bsources_%7D%29%0A%0A%23%20get%20platform%20specific%20sources%0Aif%20%28WIN32%29%0A%20%20%20%20add_definitions%28-D_CRT_SECURE_NO_DEPRECATE%29%0A%20%20%20%20set%28platformDir_%20Win32%29%0Aelse%28%29%0A%20%20%20%20set%28platformDir_%20Posix%29%0Aendif%28WIN32%29%0A%0Afile%28GLOB%20platformHeaders_%20RELATIVE%20%24%7BCMAKE_CURRENT_SOURCE_DIR%7D%20UnitTest++/%24%7BplatformDir_%7D/*.h%29%0Afile%28GLOB%20platformSources_%20RELATIVE%20%24%7BCMAKE_CURRENT_SOURCE_DIR%7D%20UnitTest++/%24%7BplatformDir_%7D/*.cpp%29%0Asource_group%28%24%7BplatformDir_%7D%20FILES%20%24%7BplatformHeaders_%7D%20%24%7BplatformSources_%7D%29%0A%0A%23%20create%20the%20lib%0Aadd_library%28UnitTest++%20STATIC%20%24%7Bheaders_%7D%20%24%7Bsources_%7D%20%24%7BplatformHeaders_%7D%20%24%7BplatformSources_%7D%29%0A%0A%23%20build%20the%20test%20runner%0Afile%28GLOB%20TEST_SRCS%20RELATIVE%20%24%7BCMAKE_CURRENT_SOURCE_DIR%7D%20tests/*.cpp%20tests/*.h%29%0Asource_group%28%20%22%22%20FILES%20%24%7BTEST_SRCS%7D%29%0Aadd_executable%28TestUnitTest++%20%24%7BTEST_SRCS%7D%29%0Ainclude_directories%28.%29%0A%0Aif%28%24%7BUTPP_USE_PLUS_SIGN%7D%29%0A%20%20%20%20set_target_properties%28TestUnitTest++%20PROPERTIES%20OUTPUT_NAME%20TestUnitTest++%29%0Aendif%28%29%0A%0Atarget_link_libraries%28TestUnitTest++%20UnitTest++%29%0A%0A%23%20run%20unit%20tests%20as%20post%20build%20step%0Aadd_custom_command%28TARGET%20TestUnitTest++%0A%20%20%20%20POST_BUILD%20COMMAND%20TestUnitTest++%0A%20%20%20%20COMMENT%20%22Running%20unit%20tests%22%29%0A%60%60%60%0Aset%2C%20file%2C%20add_library%u7B49%u7B49%u90FD%u662F%u547D%u4EE4%uFF0C%u5177%u4F53%u547D%u4EE4%u53EF%u4EE5%u53C2%u8003%u8FD9%u4E2A%u7B14%u8BB0%5BCMake%u4F7F%u7528%u603B%u7ED3%5D%28https%3A//www.evernote.com/shard/s24/nl/697376807/d90d734c-12e4-4664-a0d5-af887f4b8b10/%29%u3002%0A-%20add_library%28UnitTest++...%29%u5C06UnitTest++%u4EE3%u7801%u7F16%u8BD1%u6210%u9759%u6001%u5E93%0A-%20add_executable%28TestUnitTest++%20...%29%u751F%u6210UnitTest++%u5E94%u7528%u6587%u4EF6%0A-%20target_link_libraries%28TestUnitTest++%20UnitTest++%29%u5C06%u524D%u9762%u751F%u6210%u7684libUnitTest++.a%u94FE%u5165TestUnitTest++%u53EF%u6267%u884C%u6587%u4EF6%u3002%0A-%20add_custom_command%28TARGET%20TestUnitTest++...%29%u8FD9%u4E00%u884C%u51B3%u5B9A%u4E86%u8FD0%u884Cmake%u65F6%uFF0C%u989D%u5916%u8C03%u7528%u7684%u547D%u4EE4%u3002%0A%0A%5BCMake%u4F7F%u7528%u603B%u7ED3%5D%28https%3A//www.evernote.com/shard/s24/nl/697376807/d90d734c-12e4-4664-a0d5-af887f4b8b10/%29%u8FD9%u7BC7%u7B14%u8BB0%u91CC%u63D0%u5230%uFF1A%0A%3E%u6309%u7167%u5B98%u65B9%u6587%u6863%u7684%u8BF4%u6CD5%uFF0C%u4E0D%u5EFA%u8BAE%u4F7F%u7528file%u7684GLOB%u6307%u4EE4%u6765%u6536%u96C6%u5DE5%u7A0B%u7684%u6E90%u6587%u4EF6%uFF0C%u539F%u6587%u89E3%u91CA%u5982%u4E0B%0A%3Cfont%20color%3D%22Blue%22%3EWe%20do%20not%20recommend%20using%20GLOB%20to%20collect%20a%20list%20of%20source%20files%20from%20your%20source%20tree.%20If%20no%20CMakeLists.txt%20file%20changes%20when%20a%20source%20is%20added%20or%20removed%20then%20the%20generated%20build%20system%20cannot%20know%20when%20to%20ask%20CMake%20to%20regenerate.%3C/font%3E%0A%u5927%u610F%u5C31%u662F%uFF0CGLOB%u6536%u96C6%u7684%u6E90%u6587%u4EF6%u589E%u52A0%u6216%u5220%u9664%uFF0C%u800CCMakeLists.txt%u6CA1%u6709%u53D1%u751F%u4FEE%u6539%u65F6%uFF0CCMake%u4E0D%u80FD%u8BC6%u522B%u8FD9%u4E9B%u6587%u4EF6%u3002%u5176%u5B9E%uFF0C%u5F53CMakeLists.txt%u4F7F%u7528aux_source_directory%u548Cfile%20glob%u67E5%u627E%u5DE5%u7A0B%u6E90%u6587%u4EF6%u65F6%uFF0C%3Cb%3E%3Cfont%20color%3D%22IndianRed%22%3E%u5982%u679C%u6DFB%u52A0%u6216%u5220%u9664%u6E90%u6587%u4EF6%uFF0C%u90FD%u9700%u8981%u91CD%u65B0%u8FD0%u884CCMake%3C/font%3E%3C/b%3E%u3002%0A%0Acmake%20%u5982%u4F55%u7F16%u51FA%u53EF%u4EE5GDB%u7684%u7A0B%u5E8F%uFF1F%0A%3E%60cmake%20-DCMAKE_BUILD_TYPE%3DDebug%20CMakeList.txt%60%0A%23%23Implement%20Test%20Cases%0ATest%20Case%u7684%u7F16%u5199%u901A%u8FC7UnixTest++%u672C%u8EAB%u7684%u5B8F%u5F15%u5165%uFF1A%0A%60%60%60cpp%0A%20%20%20%20SUITE%28MyUnitTests%29%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20TEST%28StarterTest%29%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20MonoGraphCpInit%28false%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20CHECK%28true%29%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%60%60%60%0A%u8FD0%u884Cmake%u4E4B%u540E%u7684%u8F93%u51FA%u4E3A%uFF1A%0A%3ERunning%20unit%20tests%0ASuccess%3A%202%20tests%20passed.%0ATest%20time%3A%207.54%20seconds.%0A%5B100%25%5D%20Built%20target%20MyTest%0A%0ATEST%u5B8F%u5F15%u5165%u7684%u5C31%u662F%u4E00%u4E2Atest%20case%uFF0CSUITE%u5B8F%u5F15%u5165%u7684%u662F%u4E00%u4E2A%u6D4B%u8BD5%u5957%u4EF6%u3002%u9ED8%u8BA4%u60C5%u51B5%u4E0B%uFF0C%u6240%u6709%u7528SUITE%u6216%u8005TEST%u5B9A%u4E49%u7684case%u90FD%u4F1A%u88AB%u8FD0%u884C%uFF0C%u770Bmain%20code%u3002%0A%60%60%60cpp%0Aint%20main%28int%2C%20const%20char%20*%5B%5D%29%0A%7B%0A%20%20%20return%20UnitTest%3A%3ARunAllTests%28%29%3B%0A%7D%0A%60%60%60%0A%u56E0%u4E3A%u6211%u4EEC%u6709UnitTest++%u7684%u6E90%u7801%uFF0C%u5176%u5B9E%u60F3%u600E%u4E48%u6D4B%u90FD%u53EF%u4EE5%u3002%u7ECF%u8FC7%u5B66%u4E60%uFF0C%u5982%u679C%u60F3%u8C03%u7528%u6307%u5B9Acase%u7684main%20code%u53EF%u4EE5%u5982%u4E0B%u7F16%u5199%uFF1A%0A%60%60%60cpp%0A%20%20%20UnitTest%3A%3ATestReporterStdout%20reporter%3B%0A%20%20%20UnitTest%3A%3ATestRunner%20runner%28reporter%29%3B%0A%20%20%20return%20runner.RunTestsIf%28UnitTest%3A%3ATest%3A%3AGetTestList%28%29%2C%20%22MyUnitTests%22%2C%20UnitTest%3A%3ATrue%28%29%2C%200%29%3B%0A%60%60%60%0A%u8FD9%u6BB5code%u8C03%u7528%u4E86%u540D%u5B57%u53EBMyUnitTests%u7684SUITE%u4E2D%u6240%u6709case%u3002%0A%0A%23%23Linux%20Test%20Harness%0APhoenix%20FW%u91CC%u9762%u7684Linux%20Test%20Harness%u662F%u57FA%u4E8EUnixTest++%u7684%u3002%u53EF%u4EE5%u53C2%u8003%60components/GCP/JsonDataModel/linux_test_harness/Makefile%60%u3002%u5176%u4E2D%u7528%u5230%u7684UnixTest++%u662Fv1.4%u7684%uFF0C%u5E76%u6CA1%u6709%u7528%u5230cmake%u6765%u6784%u5EFA%u3002%u800C%u662F%u81EA%u5DF1%u6784%u5EFA%u7684Makefile%u6765%u7F16%u8BD1%u3002%u8FD0%u7528%u6700%u65B0%u7684cmake%u6765%u642D%u5EFAUnit%20Test%u73AF%u5883%u611F%u89C9%u66F4%u7B80%u5355%uFF0C%u800C%u4E14%u7ADF%u7136%u8FD8%u80FD%u6709%u5E73%u53F0%u79FB%u690D%u6027%uFF0C%u867D%u7136%u76EE%u524D%u5BF9%u6211%u4EEC%u6CA1%u7528%uFF0C%u4E0D%u8FC7%u8FD8%u662F%u5F88%u8D5E%u7684%u3002%u5C06UnixTest++%u7F16%u8BD1%u5230%u53EF%u7528%u8FD8%u662F%u633A%u7B80%u5355%u7684%uFF0C%u4F46%u662F%u8981%u6574%u5408Phoenix%20code%u6709%u90A3%u4E48%u4E00%u70B9%u9EBB%u70E6%u3002%u5148%u8BB2%u4E00%u4E0B%u6211%u4EEC%u7684%u4F7F%u7528%u573A%u666F%uFF0C%u6D4B%u8BD5Mono%20Graphic%20Panel%20Driver%u3002%u518D%u5C06%u5177%u4F53%u642D%u5EFA%u7684%u601D%u8DEF%u548C%u4E00%u4E9B%u5C0Ftips%u3002%0A%0A%23%23%23Mono%20Graphic%20Panel%20Driver%0A%u8FD9%u91CC%u6709%u4E00%u7BC7%u76F8%u5173%u7B14%u8BB0%5BPheonix_Display%5D%28https%3A//www.evernote.com/shard/s24/nl/2724128/9cf701b0-e736-43d5-9411-923afacf2344%29%u3002%u6211%u5176%u5B9E%u60F3%u505A%u7684%u662F%u5728Mono%20Graphic%20Panel%20Driver%u4E2D%u6DFB%u52A0%u4E00%u4E2ARecovery%20Thread%u3002%u5148%u770B%u4E0B%u6E90%u7801%u67B6%u6784%uFF1A%0A%3E%5Bbenzhou@plslx111%20phx%5D%24%20ls%20components/bsp/control_panel/%0Aabc%20%20buttonLed%20%20ccpi%20%20clcd%20%20inc%20%20%3Cfont%20color%3D%22IndianRed%22%3EmonoGraph%3C/font%3E%20%20obj%20%20pub%20%20src%0A%0A%60monoGraph%60%u4E2D%u7684%u4EE3%u7801%u4F1A%u8C03%u7528%u5230abc%u6587%u4EF6%u5939%u4E0B%u7684%u4EE3%u7801%u3002%u6240%u4EE5%60linux_test_harness%60%u5C31%u653E%u5728%u8FD9%u4E2A%u6587%u4EF6%u5939%u4E0B%u3002%u5E76%u4E14%60abc%60%u548C%60monoGraph%60%u4E2D%u7684%u4EE3%u7801%u9700%u8981%u88AB%u94FE%u63A5%u5230%u6211%u4EEC%u7684%u6D4B%u8BD5%u7A0B%u5E8F%u4E2D%u3002%0A%0A%23%23%23%u6D4B%u8BD5%u67B6%u6784%0A%u6700%u540E%u6211%u4EEC%u7684%60CMakeLists.txt%60%u5982%u4E0B%uFF1A%0A%60%60%60cmake%0Acmake_minimum_required%28VERSION%202.8.1%29%0Aproject%28MyTest%29%0A%0Aset%28CMAKE_C_COMPILER%20%22g++%22%29%0A%0A%23%20enable%20gdb%0Aset%28CMAKE_BUILD_TYPE%20%22Debug%22%29%0Aset%28CMAKE_CXX_FLAGS_DEBUG%20%22%24ENV%7BCXXFLAGS%7D%20-O0%20-Wall%20-g%20-ggdb%22%29%0Aset%28CMAKE_CXX_FLAGS_RELEASE%20%22%24ENV%7BCXXFLAGS%7D%20-O3%20-Wall%22%29%0A%0Aset%28PHX_DIR%20%22%24%7BCMAKE_CURRENT_SOURCE_DIR%7D/../../../../../%22%29%0A%0Ainclude_directories%28..%29%0Ainclude_directories%28stubs%29%0Ainclude_directories%28%24%7BPHX_DIR%7Dcomponents/bsp/control_panel/monoGraph/pub/%29%0Ainclude_directories%28%24%7BPHX_DIR%7Dcomponents/bsp/control_panel/monoGraph/inc/%29%0Ainclude_directories%28%24%7BPHX_DIR%7Dcomponents/bsp/control_panel/abc/pub/%29%0Ainclude_directories%28%24%7BPHX_DIR%7Dcomponents/bsp/control_panel/abc/inc/%29%0Ainclude_directories%28%24%7BPHX_DIR%7Dcomponents/bsp/control_panel/pub/%29%0Ainclude_directories%28%24%7BPHX_DIR%7Dcomponents/bsp/control_panel/inc/%29%0Ainclude_directories%28%24%7BPHX_DIR%7Dcomponents/primboot/pub/%29%0A%0A%23%20get%20Phoenix%20code%0A%23file%28GLOB%20PHX_SRCS%20RELATIVE%20%24%7BCMAKE_CURRENT_SOURCE_DIR%7D%20../../../src/*.cpp%20../../../src/*.c%29%0Aaux_source_directory%28%24%7BPHX_DIR%7D/components/bsp/control_panel/abc/src%20PHX_ABC%29%0Aaux_source_directory%28%24%7BPHX_DIR%7D/components/bsp/control_panel/monoGraph/src%20PHX_MONO%29%0Alist%28APPEND%20PHX_SRCS%0A%20%20%20%20%24%7BPHX_ABC%7D%0A%20%20%20%20%24%7BPHX_MONO%7D%0A%29%0Aadd_library%20%28PhxFunctions%20STATIC%20%24%7BPHX_SRCS%7D%29%0A%0A%23%20get%20the%20Phoenix%20stub%20sources%0Aaux_source_directory%28%24%7BPHX_DIR%7D/components/bsp/control_panel/linux_test_harness/mytests/stubs%20PHX_STUBS%29%0Aadd_library%20%28StubFunctions%20STATIC%20%24%7BPHX_STUBS%7D%29%0A%0A%23%20get%20the%20main%20sources%0Aaux_source_directory%28%24%7BPHX_DIR%7D/components/bsp/control_panel/linux_test_harness/mytests%20TEST_SRCS%29%0A%0Aadd_executable%28MyTest%20%24%7BTEST_SRCS%7D%29%0A%0Atarget_link_libraries%28MyTest%0A%20%20%20%20pthread%0A%20%20%20%20UnitTest++%0A%20%20%20%20PhxFunctions%0A%20%20%20%20StubFunctions%0A%29%0A%0A%23%20run%20unit%20tests%20as%20post%20build%20step%0Aadd_custom_command%28TARGET%20MyTest%0A%20%20%20%20POST_BUILD%20COMMAND%20MyTest%0A%20%20%20%20COMMENT%20%22Running%20unit%20tests%22%0A%29%0A%60%60%60%0A%u9664%u4E86UnitTest++%u6846%u67B6%u672C%u8EAB%u7684%u5E93libUnitTest++.a%u548C%u94FE%u5165%u4E86%u4E00%u4E2Apthread%u5E93%u4EE5%u5916%uFF0C%u6211%u4EEC%u8FD8%u5B9A%u4E49%u4E86%u53E6%u5916%u4E24%u4E2A%u5E93PhxFunctions%u548CStubFunctions%u3002%0A-%20PhxFunctions%u662F%u6211%u4EEC%u60F3%u6D4B%u8BD5%u7684code%uFF0C%u5373%60abc%60%u548C%60monoGraph%60%u6587%u4EF6%u5939%0A-%20StubFunctions%u662F%u4E3A%u4E86%u7F16%u8BD1%u901A%u8FC7%uFF0C%u5B9A%u4E49%u7684%u4E00%u4E9B%u5B58%u6839%u51FD%u6570%0A%0A%u4E3B%u8981%u9700%u8981%u6CE8%u610F%u7684%u662F%u600E%u4E48%u5B9A%u4E49%u8FD9%u4E9B%u5B58%u6839%u6587%u4EF6%u548C%u51FD%u6570%u3002%u6839%u636E%u6211%u7684%u7ECF%u9A8C%u548CTDD%u601D%u60F3%28%u4E0D%u8981%u4E3A%u4E86%u660E%u5929%u8BBE%u8BA1%29%uFF0C%u5E94%u8BE5%u6CE8%u610F%uFF1A%0A-%20%u5982%u679C%u6709%u627E%u4E0D%u5230%u7684%u5934%u6587%u4EF6%uFF0C%u5728stubs%u6587%u4EF6%u5939%u91CC%u52A0%u4E00%u4E2A%u7A7A%u6587%u4EF6%0A-%20%u5982%u679C%u6709%u627E%u4E0D%u5230%u7684%u7C7B%u58F0%u660E%uFF0C%u4ECEFW%u627E%u5230%u7C7B%u5B9A%u4E49%u62F7%u8D1D%u8FC7%u6765%0A-%20%u5982%u679C%u6709%u627E%u4E0D%u5230%u5B9A%u4E49%u7684%u51FD%u6570%u7684%u94FE%u63A5%u9519%u8BEF%uFF0C%u5C31%u5728stub.cpp%u4E2D%u5B9A%u4E49%u7A7A%u51FD%u6570%u3002%0A-%20%u5982%u679C%u5C06%u6765%u53D1%u73B0%u9700%u8981%u8C03%u7528%u771F%u5B9E%u7684FW%u51FD%u6570%uFF0C%u5C31%u9700%u8981%u5C06%u90A3%u4E2A%u51FD%u6570%u6240%u5728%u7684%u6E90%u7801%u6587%u4EF6%u7F16%u8BD1%u6210%u5E93%u94FE%u63A5%u8FDB%u6765%u3002

Edit

学习笔记 Programming TDD

最近在读Kent Beck的《Test-Driven Development By Example》,让我对软件开发有了新的认识。

写test并不为了测试而测试,是为了增强对程序的信心,减少焦虑。那么如何减少焦虑呢,是通过一次只关心一件事情。如何一次只关心一件事情,这就是TDD。

本书分三个部分

  1. The Money Example
  2. The xUnit Example
  3. Patterns for Test-Driven Development
    前两个部分讲了两个例子,来阐述如何进行TDD开发,最后一个部分算是总结,全书的精华所在。本文不打算讲这两个例子了,因为例子最好自己手写一遍,才可以加深了解。本文主要摘录一些TDD的模式方法(pattern)。

下面这段话来自第一章,提纲挈领:

My goal is for you to see the rhythm of Test-Driven Development (TDD), which can be summed up as follows.

  1. Quickly add a test.
  2. Run all tests and see the new one fail.
  3. Make a little change.
  4. Run all tests and see them all succeed.
  5. Refactor to remove duplication.

The surprises are likely to include.

  • How each test can cover a small increment of functionality.
  • How small and ugly the changes can be to make the new tests run.
  • How often the tests are run.
  • How many teensy-weensy steps make up the refactorings.

这就是TDD的精髓。按作者的说法就是Green/Red/Green/Refactor节奏。
这也回答了我一直以来的困扰,到底应该如何设计一段程序,到底要考虑多少种情况。按照TDD的建议就是,首先写测试,然后代码怎么快怎么来,不管有多ugly,然后如果发现重复,及时做重构。不要考虑什么扩展性之类的,这个设计方式本身就能提供。因为它提高了运行测试和重构的频率,既提升了编码者对程序的信心,也降低了不合理设计的可能性。

Chapter 25. Test-Driven Development Patterns

书中引入了很多影像图(Influence Diagram),很形象的说明了,引入TDD能给我们日常的开发工作引入什么好处。

This is a positive feedback loop.

  • The more stress you feel, the less testing you will do.
  • The less testing you do, the more errors you will make.
  • The more errors you make, the more stress you feel.
  • Rinse and repeat.

Isolated Test

能将大块的测试分割成松耦合的小测试不容易,但是好处大大的。作者说:

I never knew exactly how to achieve high cohesion and loose coupling regularly until I started writing isolated tests.

Test List

我们要怎么开始TDD呢?作者给出了很具操作性的建议:

Before you begin, write a list of all the tests you know you will have to write.
这个list上写点什么呢?

  1. Put on the list examples of every operation that you know you need to implement. 要哪些操作。
  2. for those operations that don’t already exist, put the null version of that operation on the list.哪些操作没实现,写上存根。
  3. List all of the refactorings that you think you will have to do in order to have clean code at the end of this session. 完成之后,列出需要重构的地方。而如果有大的重构的需要时,放到later list里面,把手里的事情处理完,再去处理大的重构。

看作者时刻在强调TDD就是tiny steps:

The pure form of TDD, wherein you are never more than one change away from a green bar, is like that three-out-of-four rule.

Test First

什么时候写测试?
答:在写code之前,因为一旦你开始写code,你可能就身陷泥潭,无暇顾及test case了。

One Step Test

Each test should represent one step toward your overall goal.

Starter Test

如何开始写测试?不要一开始就想写一个真正的测试,因为这样会一次面对太多的问题。作者建议尝试从一个简单的测试输入输出开始。这样以便于你从Greenxun迅速开始你的Green/Red/Green/Refactor节奏。作者举了一个产生多边形的例子:

1.Reducer r= new Reducer(new Polygon()); 
2.assertEquals(0, reducer.result().npoints);

不太懂这个问题,不过可以从中看出作者对TDD的解释。问题本身是要解一个降低多边形边数的问题。作者的启动测试就是一个“0”边多边形,那结果一定是”0”边多边形。对实际意义很荒诞,但对测试来说是一个不错的开始。符合我们的预期,简单而且又represent one step toward your overall goal

Explanation Test

这一章是在说如何不用强迫的手段让非TDD的团队成员能转向TDD。当开发者向你解释他的程序时,你都可以将之转化为test case。
本书还有一个很有特色的地方是,贯穿全文,作者总是在经意不经意间流露着各种宝贵的工作经验:如何长期保持更高效的编程?如果你是经理,你如何与你的团队成员一起工作?等等。这一章就有这样一句话:

If you’re a manager or a leader, you can’t force anyone to change the way they work.
What can you do? A simple start is to start asking for explanations in terms of test cases: “Let me see if I understand what you’re saying.

Learning Test

这一章讲了测试驱动开发针对集成第三方库的优势。

Regression Test

回归测试是指修改了旧代码后,重新进行测试以确认修改没有引入新的错误或导致其他代码产生错误。
自动回归测试将大幅降低系统测试、维护升级等阶段的成本。回归测试作为软件生命周期的一个组成部分,在整个软件测试过程中占有很大的工作量比重,软件开发的各个阶段都会进行多次回归测试。在渐进和快速迭代开发中,新版本的连续发布使回归测试进行的更加频繁,而在极端编程方法中,更是要求每天都进行若干次回归测试。因此,通过选择正确的回归测试策略来改进回归测试的效率和有效性是非常有意义的。
—— 百度百科

本书讲的回归测试概念可能略有不同。按照我的理解,作者是想说,在进行完所有的编码以后,你已经对你的要实现的所有东西完成了unknownknown的过程。那么现在,你可以回过头看看,如果这时候重新写一份测试你应该写什么。

回到你针对这个系统的最初的想法来对整个系统做一个测试。以期对整个系统有个整体的评价。

Break

劳逸结合对程序员是非常非常非常重要的!

Shower Methodology:If you know what to type, then type. If you don’t know what to type, then take a shower, and stay in the shower until you know what to type.
TDD Rule: If you don’t know what to type, then Fake It. If the
right design still isn’t clear, then Triangulate.

看看作者提倡的时间管理

  • At the scale of hours, keep a water bottle by your keyboard so that biology provides the motivation for regular breaks.
  • At the scale of a day, commitments after regular work hours can help you to stop when you need sleep before progress.
  • At the scale of a week, weekend commitments help get your conscious, energysucking thoughts off work. (My wife swears I get my best ideas on Friday evening.)
  • At the scale of a year, mandatory vacation policies help you refresh yourself completely. The French do this right—two contiguous weeks of vacation aren’t enough. You spend the first week decompressing, and the second week getting ready to go back to work. Therefore, three weeks, or better four, are necessary for you to be your most effective the rest of the year.

Do Over

What do you do when you are feeling lost? Throw away the code and start over.
作者在这一章告诉你,不要在泥潭越陷越深,不要害怕重新开始。换个伙伴,换个思路,你可能有更好的发现。

Child Test

这里说的是,如果你觉得这个测试太大,那就要把这个大的测试破成若干个小的测试,然后看看有没有进展。

Mock Object

How do you test an object that relies on an expensive or complicated resource? Create a fake version of the resource that answers constants.
If you want to use Mock Objects, you can’t easily store expensive resources in global variables.要记得清理现场,否则万一其他的代码把他当作真的object在使用,就会出大问题。所以编码最基本的准则是少用全局变量。
Mock Objects encourage you down the path of carefully considering the visibility of every object, reducing the coupling in your designs. 这里说的是,如果你用Mock Object,你就要思考我到底该不该把这个庞然大物暴露给这段代码。这种思考有助于减小耦合性。

Self Shunt

How do you test that one object communicates correctly with another? Have the object under test communicate with the test case instead of with the object it expects.

Without Self Shunt

1.# ResultListenerTest
2.def testNotification(self):
3. result= TestResult()
4. listener= ResultListener()
5. result.addListener(listener)
6. WasRun("testMethod").run(result)
7. assert 1 == listener.count
8.
9.# ResultListener
10.class ResultListener:
11. def __init__(self):
12. self.count= 0
13. def startTest(self):
14. self.count= self.count + 1

With Self Shunt

1.# ResultListenerTest
2.def testNotification(self):
3. self.count= 0
4. result= TestResult()
5. result.addListener(self)
6. WasRun("testMethod").run(result)
7. assert 1 == self.count
8.def startTest(self):
9. self.count= self.count + 1

这里举得例子不是很明白。中文译本里,将self shunt翻译成自分流,感觉也是字面翻译。我理解这里想说的是,不一定要定义一个类来做当前被测试类来做的事情,可以在test case里面用该类暴露的接口来做事情。

Self Shunt may require that you use Extract Interface to get an interface to implement. You will have to decide whether extracting the interface is easier, or if testing the existing class as a black box is easier.

这里又看晕了,self shunt跟extract interface有啥关系。

当我们注意到在我们代码结构中超过一个类使用到另一个特殊类中的同一个方法时,此时应该将该类中的方法提取出来放到一个接口(interface)中,并对外提供这个接口以便被用户类访问、使用,这样有利于打破原来多个类与这个特殊类的依赖关系。这个重构手法很容易实现,更重要的是这样做有利于松耦合。 ——网络上的解释

我觉得这里作者提到的extract interface,并不是我们说的重构里的extract interface。这里说要测试A类和B类通信,我们不直接用B类,而用test case与A类通信,以测试通信的A类端接口是否正确。extract interface说的是从B类中extract接口出来到test case中去implement。或者把B类直接当黑盒子在test case中使用。

Log String

这个很有用。有些函数或者过程是没有返回值的,我们就可以用Log String的方式来查看该过程或函数有没有被调用。类似我们看trace log一样,通过看trace log能知道机器执行的代码是否正确。

Crash Test Dummy

How do you test error code that is unlikely to be invoked? Invoke it anyway with a special object that throws an exception instead of doing real work.

Broken Test

作者经常能给出一些很实用的小经验,这里又来了,看下面。

How do you leave a programming session when you’re programming alone? Leave the last test broken.

当你要转去做另外一件事情,而手头上的事情又没结束时怎么办?让最后一个测试故意的不通过。这样下次就知道从哪儿开始,然后能迅速上手。醍醐灌顶啊!以前都是认为做另外一件事情之前要把手头上的事情弄的尽善尽美,其实不然。怎么做利于后期继续进行,那就应该怎么做。

Clean Check-in

You will occasionally find a test broken in the integration suite when you try to check in. What to do?
The simplest rule is to just throw away your work and start over. The broken test is pretty strong evidence that you didn’t know enough to program what you just programmed.
什么什么?竟然要我们扔掉我们做的,然后重新开始。就因为我们的测试和integration测试结果不一致。
大师的话总是有道理的,以后要好好考虑这一点。

Fake It (‘Til You Make It)

做假代码让测试通过,然后再一项一项地完成

Triangulate

作者提到的所谓三角法。通俗的说,就是只在必要的时候做抽象或通用化处理。通常是在TDD的重构这一步完成这个抽象的过程。再通俗点说,就是多弄几个case,再总结共同点,之后做抽象
从另一个角度,作者又说了:

I only use Triangulation when I’m really, really unsure about the correct abstraction for the calculation. Otherwise I rely on either Obvious Implementation or Fake It.

One to Many

How do you implement an operation that works with collections of objects? Implement it without the collections first, then make it work with collections.

中心思想还是一个,从显而易见的地方开始,每次只做一小步,然后一步一步到达最终的目的地。看下面的例子:

1.// 1. fake code
2.public void testSum() {
3. assertEquals(5, sum(5));
4.}
5.private int sum(int value) {
6. return value;
7.}
8.
9.// 2. add arrays
10.public void testSum() {
11. assertEquals(5, sum(5, new int[] {5}));
12.}
13.private int sum(int value, int[] values) {
14. return value;
15.}
16.
17.// 3. add real sum functionality
18.public void testSum() {
19. assertEquals(5, sum(5, new int[] {5}));
20.}
21.private int sum(int value, int[] values) {
22. int sum= 0;
23. for ( int i= 0; i<values.length; i++)
24. sum += values[i];
25. return sum;
26.}
27.
28.// 4. delete unnecessary code
29.public void testSum() {
30. assertEquals(5, sum(new int[] {5}));
31.}
32.private int sum( int[] values) {
33. int sum= 0;
34. for (int i= 0; i<values.length; i++)
35. sum += values[i];
36. return sum; sum;
37.}

细细体会其间的差别吧。

Assertion

How do you check that tests worked correctly?
Write boolean expressions that automate your judgment about whether the code worked.

作者在这一章又给出了一个很有指导意义的观点。

尽量不要做白盒测试,如果你想做,说明你的设计有问题

Fixture

这里作者抛出了一个问题,如果有多个测试有类似的代码怎么办?两个选择:

  1. test case类定义中抽象出类似setUp的函数,来做重复的工作
  2. 或者用类似代码生成器的东西来为所有需要的测试生成fixture

方法1有时候更符合DRY原则,但它的缺点是,你要记住setUp里面到底做了什么。两种方法都可以,选择更合适的。

External Fixture

所谓的外部fixture,就是要保证测试能恢复环境配置。

在所有测试开始前调setUp,所有测试结束后调tearDown。这就是External Fixture

Test Method

这一节针对如何写测试方法给出了一些建议。

  1. hierarchy
    • Module Framework
    • Class internal fixture
    • Method test case
  2. 名字长没关系,要让以后的读者有足够的线索知道为什么要写这个test
  3. 测试代码要够直觉,要够简洁(最短3行)
  4. 注释里写outline,一颗星表示一级,作者说他一般写2-3级outline
1./* Adding to tuple spaces. */ 
2./* Taking from tuple spaces. */
3./** Taking a non-existent tuple. **/
4./** Taking an existing tuple. **/
5./** Taking multiple tuples. **/
6./* Reading from tuple space. */

Exception Test

这一节说的是,如果待测的函数需要抛出异常,用如下的方法:

1.public void testMissingRate() {
2. try {
3. exchange.findRate("USD", "GBP");
4. fail(); // 如果没抛异常就fail
5. } catch (IllegalArgumentException expected) {
6. }
7.}

All Tests

How do you run all tests together? Make a suite of all the suites—one for each package, and one aggregating the package tests for the whole application.

1.public class AllTests {
2. // 这里有个main,方便直接调用程序开始测试
3. public static void main(String[] args) {
4. junit.swingui.TestRunner.run(AllTests.class);
5. }
6. // 这里有个suite包含了所有test case
7. public static Test suite() {
8. TestSuite result= new TestSuite("TFD tests");
9. result.addTestSuite(MoneyTest.class);
10. result.addTestSuite(ExchangeTest.class);
11. result.addTestSuite(IdentityRateTest.class);
12. return result;
13. }
14.}

Chapter 30. Design Patterns

作者在这一章引入了一些TDD中会用到的设计模式。有些是写测试时用的,有的是重构时用的,有的两者皆可。下面是本章所有模式的总纲。

Command

What do you do when you need the invocation of a computation to be more complicated than a simple method call?
Make an object for the computation and invoke it.

这里所说的命令是说,把要调用的方法封装成一个object,然后调用那个object

Value Object

把一些常量参数做成object来传递

When implementing a Value Object, every operation has to return a fresh object,
All Value Objects have to implement equality

1.public boolean setReadOnly() { 
2. SecurityManager guard = System.getSecurityManager();
3. if (guard != null) { ) {
4. guard.canWrite(path);
5. }
6. return fileSystem.setReadOnly(this);
7.}

上面这个例子getSecurityManager在不成功时返回Null,然后调用者再判断返回值以决定会不会用空指针来调用成员变量。
这是一个很常见的做法,这样做不好的是,通常这种判断会非常非常多,而且给人一种“会不会有哪里没判断,以后会导致crash?”的担忧。
Null Object设计模式就是让getSecurityManager返回一个特殊的object来取代Null,这样让所有的操作都统一,以减少重复代码。

Template Method

模板方法。

When you find two variants of a sequence in two subclasses, you need to gradually move them closer together. Once you’ve extracted the parts that are different from other methods, what you are left with is the Template Method.

1.// TestCase
2.public void runBare() throws Throwable {
3. setUp();
4. try {
5. runTest();
6. }
7. finally {
8. tearDown();
9. }
10.}

Pluggable Object

中文译名为插入式对象。很难理解哪里有这个所谓的“插入”。这个设计模式核心的思想就是消除if...else...的情况。
看作者的例子,解释的很清楚:

1.// Without Pluggable Object
2.Figure selected;
3.public void mouseDown() {
4. selected= findFigure();
5. if (selected != null) // 到处是null判断
6. select(selected);
7.}
8.public void mouseMove() {
9. if (selected != null)
10. move(selected);
11. else
12. moveSelectionRectangle();
13.}
14.public void mouseUp() {
15. if (selected == null)
16. selectAll();
17.}
18.
19.// With Pluggable Object
20.SelectionMode mode;
21.public void mouseDown() {
22. selected= findFigure();
23. // 全部归结到mode子类里面去处理
24. if (selected != null)
25. mode= SingleSelection(selected);
26. else
27. mode= MultipleSelection();
28.}
29.public void mouseMove() {
30. mode.mouseMove();
31.}
32.public void mouseUp() {
33. mode.mouseUp();
34.}

Pluggable Selector

这一节的设计模式和上一节的极为类似。这次要处理掉的是switch...case...而又不用子类继承的方式来动态调用。这里使用的方法叫反射。

反射:通过自身的属性(如类名,方法名)来做相关逻辑

同样看作者的例子

1.void print() { 
2. Method runMethod= getClass().getMethod(printMessage, null);
3. runMethod.invoke(this, new Class[0]);
4.}

看到了没getClass,getMethod,这就是反射。有了这个函数既不需要switch来调用不同的printxxx函数(printHtml, printXml…),也不需要定义子类来调用不同的print函数。
作者在本节最后给出了一段忠告:

Pluggable Selector can definitely be overused. The biggest problem with it is tracing code to see whether a method is invoked. Use Pluggable Selector only when you are cleaning up a fairly straightforward situation in which each of a bunch of subclasses has only one method.

Factory Method

作者说工厂方法提供了灵活性。我是没看出来。对比下面两段代码,这里增加了灵活性?

1.public void testMultiplication() {
2. Dollar five= Dollar five= new Dollar(5);
3. assertEquals(new Dollar(10), five.times(2));
4. assertEquals(new Dollar(15), five.times(3));
5.}
6.
7.public void testMultiplication() {
8. Dollar five = Money.dollar(5); // 工厂方法
9. assertEquals(new Dollar(10), five.times(2));
10. assertEquals(new Dollar(15), five.times(3));
11.}
12.// Money
13.static Dollar dollar( Dollar dollar(int amount) {
14. return new Dollar(amount);
15.}

作者最后给出了一点忠告

You have to remember that the method is really creating an object, even though it doesn’t look like a constructor. Use Factory Method only when you need the flexibility it creates.

Imposter

中文译名为冒名顶替。根据文中的阐述,我理解这里讲的不是一种设计模式,而只是一种模式,一种特殊情况。指的是如果某一段代码,只将其中的一个名字(类、方法或者变量等等)替换成另外一个名字,就能得到一段新的代码,称为冒名顶替。发现可以Imposter的情况,其实就是发现重复。

1.// 下面这两段代码就只是将RectangleFigure换成OvalFigure,此是为Imposter冒名顶替。
2.testRectangle() {
3. Drawing d= new Drawing();
4. d.addFigure(new RectangleFigure(0, 10, 50, 100)); RectangleFigure(0, 10, 50, 100));
5. RecordingMedium brush= new RecordingMedium();
6. d.display(brush);
7. assertEquals("rectangle 0 10 50 100\n", brush.log());
8.}
9.
10.testOval() {
11. Drawing d= new Drawing();
12. d.addFigure(new OvalFigure(0, 10, 50, 100));
13. RecordingMedium brush= new RecordingMedium();
14. d.display(brush);
15. assertEquals("oval 0 10 50 100\n", brush.log());
16.}

作者还提到另外两个在重构中经常用到的设计模式,本质其实就是Imposter —— Null Object & Composite。

Composite

根据上一章作者的观点,组合其实就是一种冒名顶替。作者在本节开头说了:

How do you implement an object whose behavior is the composition of the behavior of a list of other objects? Make it an Imposter for the component objects.

让这个对象称为组成他的对象的冒名顶替者。说起来很拗口。看例子:

1.// Transaction
2.Transaction(Money value) {
3. this.value= value;
4.}
5.// Account
6.Transaction transactions[];
7.Money balance() {
8. Money sum= Money.zero();
9. for (int i= 0; i < transactions.length; i++)
10. sum= sum.plus(transactions[i].value);
11. return sum;
12.}

这个写法的缺点是,当要算OverallAccount时,没辙了,要定义一个新类来处理。一个OverallAccount是一组Account。但OverallAccount其实要做的也是一种累加,和Account做的并没有什么区别。这就是重复。
解决的办法就是所谓的Composite。把要累加的东西抽象出来称为Holdings,Account累加的是Holdings,OverallAccount累加的也是Holdings。

1.Holding holdings[];
2.Money balance() {
3. Money sum= Money.zero();
4. for ( (int i= 0; i < holdings.length; i++)
5. sum= sum.plus(holdings[i].balance());
6. return sum;
7.}

写到这里,我仍然不是特别明白何为Composite(拼合,复合的意思)。但我们是站在巨人的肩膀上,对比阅读了中文译本后有了一点点感觉。这个设计模式的中文译名为-递归组合。所谓递归,应当指的是,通过概念上的抽象,将一个大的混合类写成一些更抽象的概念的组合。例如:

OvreallAccount <- Account <- Transactions
OverallAccount <- Holding & Account <-Holding

通过递归地将OverallAccount冒名顶替成Holding,从而使OverallAccount成为一个Account来达到消除重复。
作者在本节最后给出了何时使用Composite的建议:

As is obvious from this discussion, I’m still not able to articulate how to guess when a collection of objects is just a collection of objects and when you really have a Composite. The good news is, since you’re getting good at refactoring, the moment the duplication appears, you can introduce Composite and watch program complexity disappear.

Collecting Parameter

看不懂,不知所云

Singleton

这个就是很著名的单例模式,经常会用到。作者没有详加阐释,只是告诫读者不要用全局变量(global variable)。我猜作者的意思是,如果要用全局变量,就用单例模式的类对象吧。

Chapter 31. Refactoring

在TDD中,重构要做的就是,保证所有测试通过的情况下,降低重复(code之间,code与test之间)。

Reconcile Differences

  • How do you unify two similar looking pieces of code? Gradually bring them closer. Unify them only when they are absolutely identical. 不要强制整合,等他们完全相等了再说
  • Such a leap-of-faith refactoring is exactly what we’re trying to avoid with our strategy of small steps and concrete feedback. Although you can’t always avoid leapy refactorings, you can reduce their incidence. 不要做跨越式的重构。
  • Sometimes you need to approach reconciling differences backward—that is, think about how the last step of the change could be trivial, then work backward. 反向思考,尽量让每一步都非常小(trivial),这样方便回退。

Isolate Change

隔离改动,也就是我们通常做的一些抽象封装。把变化的部分用接口包装起来,这样就达到了隔离的效果。

Some possible ways to isolate change are Extract Method (the most common), Extract Object, and Method Object.

Migrate Data

不知所云

Extract Method

How do you make a long, complicated method easier to read? Turn a small part of it into a separate method and call the new method. 将一个大函数打碎,从中提取一些方法出来。

How

  1. Find a region of the method that would make sense as its own method. Bodies of
    loop, whole loops, and branches of conditionals are common candidates for extraction.
  2. Make sure that there are no assignments to temporary variables declared outside the
    scope of the region to be extracted.
  3. Copy the code from the old method to the new method. Compile it.
  4. For each temporary variable or parameter of the original method used in the new
    method, add a parameter to the new method.
  5. Call the new method from the original method.

Why

  1. 增强可读性
  2. 消除重复,当你发现有两个大函数,有一部分是相同的,那就可以提取出来称为新的方法。
  3. 通过inline把一堆函数放在一起,然后看看哪里可以extract (这一点我感觉应该放在How里面,作者是想承上启下,将inline吧)

Inline Method

inline很玄乎,其实就是把函数的代码复制到调用的地方。好处是什么,作者有话说:

You might like the second version better, or you might not. The point to note here is that you can use Inline Method to play around with the flow of control.
其实就是让你把抽象出来的东西放到一起重新抽象,看看会不会有更好的结果。这也是一种重构的方法。

How

如何将一个类抽象成一个interface。所谓的interface就是只有纯虚函数的类。

  1. Declare an interface. Sometimes the name of the existing class should be the name of the interface, in which case you should first rename the class.
  2. Have the existing class implement the interface.
  3. Add the necessary methods to the interface, expanding the visibility of the methods in the class if necessary.
  4. Change type declarations from the class to the interface where possible.

Why

这里提到了两种情况:

  1. 当你需要抽象出父类时
  2. 当你用了Mock Object,现在要为这个mock object抽象出一个真正的接口时。作者这里提到了一个小技巧,在这种情况下命名是一个头疼的问题。不妨就把新的interface称为IXXX,例如,原来的叫File,新的接口就称为IFile

Move Method

其实就是把一个类的方法移到另外一个类里面。但作者提到了一种情况下,这个方法不适用。就是这个方法修改了原先那个类的数据。

Method Object

这个话题好像很新鲜,之前看到过很多次,但并没有深究。先看看How:

How

  1. Create an object with the same parameters as the method.
  2. Make the local variables also instance variables of the object.
  3. Create one method called run(), whose body is the same as the body of the original method.
  4. In the original method, create a new object and invoke run().

有什么好处?

Why

两个好处

  1. 在添加复杂逻辑的时候,如果有对一个过程有多种方法,那创建一个方法对象,再扩展起来就很简单
  2. 在一个复杂函数里extract interface的时候,通常一段code需要5,6个参数,这样的话,虽然函数抽取出来了,但是写法并没有简化多少。这时候如果有方法对象,则在类里面是一个独立的名字空间,会提供额外的便利。

Add Parameter

就是加一个参数,没什么特别的

Method Parameter to Constructor Parameter

就是把方法调用的参数,变成类成员变量。为什么要这么做?

If you pass the same parameter to several different methods in the same object, then you can simplify the API by passing the parameter once (eliminating duplication). You can run this refactoring in reverse if you find that an instance variable is used in only one method.

这个方法简单实用。

Chapter 32. Mastering TDD

经过31章的学习,作者认为我们已经完完全全有能力成为一个合格的TDD程序员了。在这一章里,作者又总结出很多TDD初学者的问题,给予回答。答案都是一些很实用的忠告,特摘取如下。

What don’t you have to test?

作者并没有盲目强调测试重要,也没有说任何地方都要写测试。作者说:

Unless you have reason to distrust it, don’t test code from others.
“Write tests until fear is transformed into boredom.”

只在你不相信你的code时,对他进行测试。测试也不是为了测试而测试,或者为了代码的健壮,测试只是为了减少你的焦虑。让你不要从担心变成烦躁,仅此而已。
哎!真是醍醐灌顶!相见恨晚!

How do you know if you have good tests?

什么样的测试不好?

  1. Long setup code. 如果为了做一个简单的测试,可能只有几行断言,而为此创建一个巨大的对象,那说明哪里肯定出问题了。
  2. Setup duplication. 如果你发现有几个测试拥有相同或相似的setup代码,那说明你的测试中有重复的地方。
  3. Long running tests. TDD tests that run a long time won’t be run often. Suites that take longer than ten minutes
    inevitably get trimmed. 一个测试套件不要超过十分钟。否则,反复运行的机会就会急剧降低。
  4. Fragile tests. Tests that break unexpectedly suggest that one part of the application is surprisingly affecting another part. 如果测试被无情打断,说明你的程序里有耦合。

How does TDD lead to frameworks?

又被醍醐灌顶了!

TDD appears to stand this advice on its head: “Code for tomorrow, design for today.” Here’s what happens in practice. 不要为未来考虑那么多,简单的让测试通过,然后及时的做重构消除重复,反而能达到更好的设计框架(frameworks)。

How much feedback do you need?

这里说的和之前一直强调的思想很一致

If our knowledge of the implementation gives us confidence even without a test, then we will not write that test.

When should you delete tests?

  1. The first criterion for your tests is confidence. Never delete a test if it reduces your confidence in the behavior of the system.
  2. The second criterion is communication. If you have two tests that exercise the same path through the code, but they speak to different scenarios for a reader, leave them alone.

How do you switch to TDD midstream?

这也是我想问的,作者的回答是这本书就是为了这个而写的。

There is a whole book (or books) to be written about switching to TDD when you have lots of code. What follows is necessarily only a teaser.

怎么做?

  1. So first we have to decide to limit the scope of our changes.
  2. Second, we have to break the deadlock between tests and refactoring.
    1. 怎么获得feedback
      1) We can get feedback other ways than with tests, like working very carefully and with a partner.
      2) We can get feedback at a gross level, like system-level tests that we know aren’t adequate but give us some confidence. With this feedback, we can make the areas we have to change more accepting of change.

How does TDD relate to the practices of Extreme Programming?

名词解释:Extreme Programming - XP极限编程

极限编程是一个轻量级的、灵巧的软件开发方法;同时它也是一个非常严谨和周密的方法。它的基础和价值观是交流、朴素、反馈和勇气;即,任何一个软件项目都可以从四个方面入手进行改善:加强交流;从简单做起;寻求反馈;勇于实事求是。XP是一种近螺旋式的开发方法,它将复杂的开发过程分解为一个个相对比较简单的小周期;通过积极的交流、反馈以及其它一系列的方法,开发人员和客户可以非常清楚开发进度、变化、待解决的问题和潜在的困难等,并根据实际情况及时地调整开发过程。

TDD与XP的共通之处:

  • Pairing
  • Work fresh
  • Continuous integration
  • Simple design
  • Refactoring
  • Continuous delivery
%23Test-Driven%20Development%0A@%28%u5B66%u4E60%u7B14%u8BB0%29%5BProgramming%2C%20TDD%5D%0A%0A%5BTOC%5D%0A%0A%u6700%u8FD1%u5728%u8BFBKent%20Beck%u7684%u300ATest-Driven%20Development%20By%20Example%u300B%uFF0C%u8BA9%u6211%u5BF9%u8F6F%u4EF6%u5F00%u53D1%u6709%u4E86%u65B0%u7684%u8BA4%u8BC6%u3002%0A%3E%u5199test%u5E76%u4E0D%u4E3A%u4E86%u6D4B%u8BD5%u800C%u6D4B%u8BD5%uFF0C%u662F%u4E3A%u4E86%u589E%u5F3A%u5BF9%u7A0B%u5E8F%u7684%u4FE1%u5FC3%uFF0C%u51CF%u5C11%u7126%u8651%u3002%u90A3%u4E48%u5982%u4F55%u51CF%u5C11%u7126%u8651%u5462%uFF0C%u662F%u901A%u8FC7%u4E00%u6B21%u53EA%u5173%u5FC3%u4E00%u4EF6%u4E8B%u60C5%u3002%u5982%u4F55%u4E00%u6B21%u53EA%u5173%u5FC3%u4E00%u4EF6%u4E8B%u60C5%uFF0C%u8FD9%u5C31%u662FTDD%u3002%0A%0A%u672C%u4E66%u5206%u4E09%u4E2A%u90E8%u5206%0A1.%20The%20Money%20Example%0A2.%20The%20xUnit%20Example%0A3.%20Patterns%20for%20Test-Driven%20Development%0A%u524D%u4E24%u4E2A%u90E8%u5206%u8BB2%u4E86%u4E24%u4E2A%u4F8B%u5B50%uFF0C%u6765%u9610%u8FF0%u5982%u4F55%u8FDB%u884CTDD%u5F00%u53D1%uFF0C%u6700%u540E%u4E00%u4E2A%u90E8%u5206%u7B97%u662F%u603B%u7ED3%uFF0C%u5168%u4E66%u7684%u7CBE%u534E%u6240%u5728%u3002%u672C%u6587%u4E0D%u6253%u7B97%u8BB2%u8FD9%u4E24%u4E2A%u4F8B%u5B50%u4E86%uFF0C%u56E0%u4E3A%u4F8B%u5B50%u6700%u597D%u81EA%u5DF1%u624B%u5199%u4E00%u904D%uFF0C%u624D%u53EF%u4EE5%u52A0%u6DF1%u4E86%u89E3%u3002%u672C%u6587%u4E3B%u8981%u6458%u5F55%u4E00%u4E9BTDD%u7684%u6A21%u5F0F%u65B9%u6CD5%28pattern%29%u3002%0A%0A%u4E0B%u9762%u8FD9%u6BB5%u8BDD%u6765%u81EA%u7B2C%u4E00%u7AE0%uFF0C%u63D0%u7EB2%u6308%u9886%uFF1A%0A%3EMy%20goal%20is%20for%20you%20to%20see%20the%20rhythm%20of%20Test-Driven%20Development%20%28TDD%29%2C%20which%20can%20be%20summed%20up%20as%20follows.%0A%3E%201.%20Quickly%20add%20a%20test.%0A%3E%202.%20Run%20all%20tests%20and%20see%20the%20new%20one%20fail.%0A%3E%203.%20Make%20a%20little%20change.%0A%3E%204.%20Run%20all%20tests%20and%20see%20them%20all%20succeed.%0A%3E%205.%20Refactor%20to%20remove%20duplication.%0A%0A%3EThe%20surprises%20are%20likely%20to%20include.%0A%3E%20*%20How%20each%20test%20can%20cover%20a%20**small%20increment**%20of%20functionality.%0A%3E%20*%20How%20**small%20and%20ugly%20the%20changes**%20can%20be%20to%20make%20the%20new%20tests%20run.%0A%3E%20*%20How%20often%20the%20tests%20are%20run.%0A%3E%20*%20How%20many%20teensy-weensy%20steps%20make%20up%20the%20refactorings.%0A%0A%u8FD9%u5C31%u662FTDD%u7684%u7CBE%u9AD3%u3002%u6309%u4F5C%u8005%u7684%u8BF4%u6CD5%u5C31%u662F%60Green/Red/Green/Refactor%60%u8282%u594F%u3002%0A%u8FD9%u4E5F%u56DE%u7B54%u4E86%u6211%u4E00%u76F4%u4EE5%u6765%u7684%u56F0%u6270%uFF0C%u5230%u5E95%u5E94%u8BE5%u5982%u4F55%u8BBE%u8BA1%u4E00%u6BB5%u7A0B%u5E8F%uFF0C%u5230%u5E95%u8981%u8003%u8651%u591A%u5C11%u79CD%u60C5%u51B5%u3002%u6309%u7167TDD%u7684%u5EFA%u8BAE%u5C31%u662F%uFF0C%u9996%u5148%u5199%u6D4B%u8BD5%uFF0C%u7136%u540E%u4EE3%u7801%u600E%u4E48%u5FEB%u600E%u4E48%u6765%uFF0C%u4E0D%u7BA1%u6709%u591Augly%uFF0C%u7136%u540E%u5982%u679C%u53D1%u73B0%u91CD%u590D%uFF0C%u53CA%u65F6%u505A%u91CD%u6784%u3002%u4E0D%u8981%u8003%u8651%u4EC0%u4E48%u6269%u5C55%u6027%u4E4B%u7C7B%u7684%uFF0C%u8FD9%u4E2A%u8BBE%u8BA1%u65B9%u5F0F%u672C%u8EAB%u5C31%u80FD%u63D0%u4F9B%u3002%u56E0%u4E3A%u5B83%u63D0%u9AD8%u4E86%u8FD0%u884C%u6D4B%u8BD5%u548C%u91CD%u6784%u7684%u9891%u7387%uFF0C%u65E2%u63D0%u5347%u4E86%u7F16%u7801%u8005%u5BF9%u7A0B%u5E8F%u7684%u4FE1%u5FC3%uFF0C%u4E5F%u964D%u4F4E%u4E86%u4E0D%u5408%u7406%u8BBE%u8BA1%u7684%u53EF%u80FD%u6027%u3002%0A%0A%23%23Chapter%2025.%20Test-Driven%20Development%20Patterns%0A%u4E66%u4E2D%u5F15%u5165%u4E86%u5F88%u591A%u5F71%u50CF%u56FE%28Influence%20Diagram%29%uFF0C%u5F88%u5F62%u8C61%u7684%u8BF4%u660E%u4E86%uFF0C%u5F15%u5165TDD%u80FD%u7ED9%u6211%u4EEC%u65E5%u5E38%u7684%u5F00%u53D1%u5DE5%u4F5C%u5F15%u5165%u4EC0%u4E48%u597D%u5904%u3002%0A%21%5BAlt%20text%5D%28./1455616022147.png%29%0A%3EThis%20is%20a%20positive%20feedback%20loop.%20%0A%3E-%20The%20more%20stress%20you%20feel%2C%20the%20less%20testing%20you%20will%20do.%20%0A%3E-%20The%20less%20testing%20you%20do%2C%20the%20more%20errors%20you%20will%20make.%20%0A%3E-%20The%20more%20errors%20you%20make%2C%20the%20more%20stress%20you%20feel.%20%0A%3E-%20Rinse%20and%20repeat.%0A%0A%23%23%23Isolated%20Test%0A%u80FD%u5C06%u5927%u5757%u7684%u6D4B%u8BD5%u5206%u5272%u6210%u677E%u8026%u5408%u7684%u5C0F%u6D4B%u8BD5%u4E0D%u5BB9%u6613%uFF0C%u4F46%u662F%u597D%u5904%u5927%u5927%u7684%u3002%u4F5C%u8005%u8BF4%uFF1A%0A%3EI%20never%20knew%20exactly%20how%20to%20achieve%20high%20cohesion%20and%20loose%20coupling%20regularly%20until%20I%20started%20writing%20isolated%20tests.%0A%23%23%23Test%20List%0A%u6211%u4EEC%u8981%u600E%u4E48%u5F00%u59CBTDD%u5462%uFF1F%u4F5C%u8005%u7ED9%u51FA%u4E86%u5F88%u5177%u64CD%u4F5C%u6027%u7684%u5EFA%u8BAE%uFF1A%0A%3EBefore%20you%20begin%2C%20write%20a%20list%20of%20all%20the%20tests%20you%20know%20you%20will%20have%20to%20write.%0A%u8FD9%u4E2Alist%u4E0A%u5199%u70B9%u4EC0%u4E48%u5462%uFF1F%0A%3E%201.%20Put%20on%20the%20list%20examples%20of%20every%20operation%20that%20you%20know%20you%20need%20to%20implement.%20%u8981%u54EA%u4E9B%u64CD%u4F5C%u3002%0A%3E%202.%20for%20those%20operations%20that%20don%27t%20already%20exist%2C%20put%20the%20null%20version%20of%20that%20operation%20on%20the%20list.%u54EA%u4E9B%u64CD%u4F5C%u6CA1%u5B9E%u73B0%uFF0C%u5199%u4E0A%u5B58%u6839%u3002%0A%3E%203.%20List%20all%20of%20the%20refactorings%20that%20you%20think%20you%20will%20have%20to%20do%20in%20order%20to%20have%20clean%20code%20at%20the%20end%20of%20this%20session.%20%u5B8C%u6210%u4E4B%u540E%uFF0C%u5217%u51FA%u9700%u8981%u91CD%u6784%u7684%u5730%u65B9%u3002%u800C%u5982%u679C%u6709%u5927%u7684%u91CD%u6784%u7684%u9700%u8981%u65F6%uFF0C%u653E%u5230later%20list%u91CC%u9762%uFF0C%u628A%u624B%u91CC%u7684%u4E8B%u60C5%u5904%u7406%u5B8C%uFF0C%u518D%u53BB%u5904%u7406%u5927%u7684%u91CD%u6784%u3002%0A%0A%u770B%u4F5C%u8005%u65F6%u523B%u5728%u5F3A%u8C03TDD%u5C31%u662Ftiny%20steps%uFF1A%0A%3E%20The%20pure%20form%20of%20TDD%2C%20wherein%20you%20are%20never%20more%20than%20one%20change%20away%20from%20a%20green%20bar%2C%20is%20like%20that%20three-out-of-four%20rule.%0A%23%23%23Test%20First%0A%u4EC0%u4E48%u65F6%u5019%u5199%u6D4B%u8BD5%uFF1F%0A%u7B54%uFF1A%u5728%u5199code%u4E4B%u524D%uFF0C%u56E0%u4E3A%u4E00%u65E6%u4F60%u5F00%u59CB%u5199code%uFF0C%u4F60%u53EF%u80FD%u5C31%u8EAB%u9677%u6CE5%u6F6D%uFF0C%u65E0%u6687%u987E%u53CAtest%20case%u4E86%u3002%0A%0A%23%23Chapter%2026.%20Red%20Bar%20Patterns%0A%23%23%23One%20Step%20Test%0A%3EEach%20test%20should%20represent%20one%20step%20toward%20your%20overall%20goal.%0A%23%23%23Starter%20Test%0A%u5982%u4F55%u5F00%u59CB%u5199%u6D4B%u8BD5%uFF1F%u4E0D%u8981%u4E00%u5F00%u59CB%u5C31%u60F3%u5199%u4E00%u4E2A%u771F%u6B63%u7684%u6D4B%u8BD5%uFF0C%u56E0%u4E3A%u8FD9%u6837%u4F1A%u4E00%u6B21%u9762%u5BF9%u592A%u591A%u7684%u95EE%u9898%u3002%u4F5C%u8005%u5EFA%u8BAE%u5C1D%u8BD5%u4ECE%u4E00%u4E2A%u7B80%u5355%u7684%u6D4B%u8BD5%u8F93%u5165%u8F93%u51FA%u5F00%u59CB%u3002%u8FD9%u6837%u4EE5%u4FBF%u4E8E%u4F60%u4ECEGreenxun%u8FC5%u901F%u5F00%u59CB%u4F60%u7684Green/Red/Green/Refactor%u8282%u594F%u3002%u4F5C%u8005%u4E3E%u4E86%u4E00%u4E2A%u4EA7%u751F%u591A%u8FB9%u5F62%u7684%u4F8B%u5B50%uFF1A%0A%60%60%60%20java%0AReducer%20r%3D%20new%20Reducer%28new%20Polygon%28%29%29%3B%20%0AassertEquals%280%2C%20reducer.result%28%29.npoints%29%3B%0A%60%60%60%0A%u4E0D%u592A%u61C2%u8FD9%u4E2A%u95EE%u9898%uFF0C%u4E0D%u8FC7%u53EF%u4EE5%u4ECE%u4E2D%u770B%u51FA%u4F5C%u8005%u5BF9TDD%u7684%u89E3%u91CA%u3002%u95EE%u9898%u672C%u8EAB%u662F%u8981%u89E3%u4E00%u4E2A%u964D%u4F4E%u591A%u8FB9%u5F62%u8FB9%u6570%u7684%u95EE%u9898%u3002%u4F5C%u8005%u7684%u542F%u52A8%u6D4B%u8BD5%u5C31%u662F%u4E00%u4E2A%u201C0%u201D%u8FB9%u591A%u8FB9%u5F62%uFF0C%u90A3%u7ED3%u679C%u4E00%u5B9A%u662F%220%22%u8FB9%u591A%u8FB9%u5F62%u3002%u5BF9%u5B9E%u9645%u610F%u4E49%u5F88%u8352%u8BDE%uFF0C%u4F46%u5BF9%u6D4B%u8BD5%u6765%u8BF4%u662F%u4E00%u4E2A%u4E0D%u9519%u7684%u5F00%u59CB%u3002%u7B26%u5408%u6211%u4EEC%u7684%u9884%u671F%uFF0C%u7B80%u5355%u800C%u4E14%u53C8%60represent%20one%20step%20toward%20your%20overall%20goal%60%u3002%0A%23%23%23Explanation%20Test%0A%u8FD9%u4E00%u7AE0%u662F%u5728%u8BF4%u5982%u4F55%u4E0D%u7528%u5F3A%u8FEB%u7684%u624B%u6BB5%u8BA9%u975ETDD%u7684%u56E2%u961F%u6210%u5458%u80FD%u8F6C%u5411TDD%u3002%u5F53%u5F00%u53D1%u8005%u5411%u4F60%u89E3%u91CA%u4ED6%u7684%u7A0B%u5E8F%u65F6%uFF0C%u4F60%u90FD%u53EF%u4EE5%u5C06%u4E4B%u8F6C%u5316%u4E3Atest%20case%u3002%0A%u672C%u4E66%u8FD8%u6709%u4E00%u4E2A%u5F88%u6709%u7279%u8272%u7684%u5730%u65B9%u662F%uFF0C%u8D2F%u7A7F%u5168%u6587%uFF0C%u4F5C%u8005%u603B%u662F%u5728%u7ECF%u610F%u4E0D%u7ECF%u610F%u95F4%u6D41%u9732%u7740%u5404%u79CD%u5B9D%u8D35%u7684%u5DE5%u4F5C%u7ECF%u9A8C%uFF1A%u5982%u4F55%u957F%u671F%u4FDD%u6301%u66F4%u9AD8%u6548%u7684%u7F16%u7A0B%uFF1F%u5982%u679C%u4F60%u662F%u7ECF%u7406%uFF0C%u4F60%u5982%u4F55%u4E0E%u4F60%u7684%u56E2%u961F%u6210%u5458%u4E00%u8D77%u5DE5%u4F5C%uFF1F%u7B49%u7B49%u3002%u8FD9%u4E00%u7AE0%u5C31%u6709%u8FD9%u6837%u4E00%u53E5%u8BDD%uFF1A%0A%3EIf%20you%27re%20a%20manager%20or%20a%20leader%2C%20you%20can%27t%20force%20anyone%20to%20change%20the%20way%20they%20work.%0A%3EWhat%20can%20you%20do%3F%20A%20simple%20start%20is%20to%20start%20asking%20for%20explanations%20in%20terms%20of%20test%20cases%3A%20%22Let%20me%20see%20if%20I%20understand%20what%20you%27re%20saying.%0A%0A%23%23%23Learning%20Test%0A%u8FD9%u4E00%u7AE0%u8BB2%u4E86%u6D4B%u8BD5%u9A71%u52A8%u5F00%u53D1%u9488%u5BF9%u96C6%u6210%u7B2C%u4E09%u65B9%u5E93%u7684%u4F18%u52BF%u3002%0A%23%23%23Regression%20Test%0A%3E**%u56DE%u5F52%u6D4B%u8BD5**%u662F%u6307%u4FEE%u6539%u4E86%u65E7%u4EE3%u7801%u540E%uFF0C%u91CD%u65B0%u8FDB%u884C%u6D4B%u8BD5%u4EE5%u786E%u8BA4%u4FEE%u6539%u6CA1%u6709%u5F15%u5165%u65B0%u7684%u9519%u8BEF%u6216%u5BFC%u81F4%u5176%u4ED6%u4EE3%u7801%u4EA7%u751F%u9519%u8BEF%u3002%0A%3E%u81EA%u52A8%u56DE%u5F52%u6D4B%u8BD5%u5C06%u5927%u5E45%u964D%u4F4E%u7CFB%u7EDF%u6D4B%u8BD5%u3001%u7EF4%u62A4%u5347%u7EA7%u7B49%u9636%u6BB5%u7684%u6210%u672C%u3002%u56DE%u5F52%u6D4B%u8BD5%u4F5C%u4E3A%u8F6F%u4EF6%u751F%u547D%u5468%u671F%u7684%u4E00%u4E2A%u7EC4%u6210%u90E8%u5206%uFF0C%u5728%u6574%u4E2A%u8F6F%u4EF6%u6D4B%u8BD5%u8FC7%u7A0B%u4E2D%u5360%u6709%u5F88%u5927%u7684%u5DE5%u4F5C%u91CF%u6BD4%u91CD%uFF0C%u8F6F%u4EF6%u5F00%u53D1%u7684%u5404%u4E2A%u9636%u6BB5%u90FD%u4F1A%u8FDB%u884C%u591A%u6B21%u56DE%u5F52%u6D4B%u8BD5%u3002%u5728%u6E10%u8FDB%u548C%u5FEB%u901F%u8FED%u4EE3%u5F00%u53D1%u4E2D%uFF0C%u65B0%u7248%u672C%u7684%u8FDE%u7EED%u53D1%u5E03%u4F7F%u56DE%u5F52%u6D4B%u8BD5%u8FDB%u884C%u7684%u66F4%u52A0%u9891%u7E41%uFF0C%u800C%u5728%u6781%u7AEF%u7F16%u7A0B%u65B9%u6CD5%u4E2D%uFF0C%u66F4%u662F%u8981%u6C42%u6BCF%u5929%u90FD%u8FDB%u884C%u82E5%u5E72%u6B21%u56DE%u5F52%u6D4B%u8BD5%u3002%u56E0%u6B64%uFF0C%u901A%u8FC7%u9009%u62E9%u6B63%u786E%u7684%u56DE%u5F52%u6D4B%u8BD5%u7B56%u7565%u6765%u6539%u8FDB%u56DE%u5F52%u6D4B%u8BD5%u7684%u6548%u7387%u548C%u6709%u6548%u6027%u662F%u975E%u5E38%u6709%u610F%u4E49%u7684%u3002%0A%u2014%u2014%20**%u767E%u5EA6%u767E%u79D1**%0A%0A%u672C%u4E66%u8BB2%u7684%u56DE%u5F52%u6D4B%u8BD5%u6982%u5FF5%u53EF%u80FD%u7565%u6709%u4E0D%u540C%u3002%u6309%u7167%u6211%u7684%u7406%u89E3%uFF0C%u4F5C%u8005%u662F%u60F3%u8BF4%uFF0C%u5728%u8FDB%u884C%u5B8C%u6240%u6709%u7684%u7F16%u7801%u4EE5%u540E%uFF0C%u4F60%u5DF2%u7ECF%u5BF9%u4F60%u7684%u8981%u5B9E%u73B0%u7684%u6240%u6709%u4E1C%u897F%u5B8C%u6210%u4E86unknown%24%5Crightarrow%20%24known%u7684%u8FC7%u7A0B%u3002%u90A3%u4E48%u73B0%u5728%uFF0C%u4F60%u53EF%u4EE5%u56DE%u8FC7%u5934%u770B%u770B%uFF0C%u5982%u679C%u8FD9%u65F6%u5019%u91CD%u65B0%u5199%u4E00%u4EFD%u6D4B%u8BD5%u4F60%u5E94%u8BE5%u5199%u4EC0%u4E48%u3002%0A%0A%3E%u56DE%u5230%u4F60%u9488%u5BF9%u8FD9%u4E2A%u7CFB%u7EDF%u7684%u6700%u521D%u7684%u60F3%u6CD5%u6765%u5BF9%u6574%u4E2A%u7CFB%u7EDF%u505A%u4E00%u4E2A%u6D4B%u8BD5%u3002%u4EE5%u671F%u5BF9%u6574%u4E2A%u7CFB%u7EDF%u6709%u4E2A%u6574%u4F53%u7684%u8BC4%u4EF7%u3002%0A%23%23%23Break%0A%u52B3%u9038%u7ED3%u5408%u5BF9%u7A0B%u5E8F%u5458%u662F%u975E%u5E38%u975E%u5E38%u975E%u5E38%u91CD%u8981%u7684%uFF01%0A%3E%20**Shower%20Methodology**%3AIf%20you%20know%20what%20to%20type%2C%20then%20type.%20If%20you%20don%27t%20know%20what%20to%20type%2C%20then%20take%20a%20shower%2C%20and%20stay%20in%20the%20shower%20until%20you%20know%20what%20to%20type.%0A%3E**TDD%20Rule**%3A%20If%20you%20don%27t%20know%20what%20to%20type%2C%20then%20Fake%20It.%20If%20the%0Aright%20design%20still%20isn%27t%20clear%2C%20then%20Triangulate.%20%0A%0A%u770B%u770B%u4F5C%u8005%u63D0%u5021%u7684%u65F6%u95F4%u7BA1%u7406%0A%3E*%20At%20the%20scale%20of%20hours%2C%20keep%20a%20water%20bottle%20by%20your%20keyboard%20so%20that%20biology%20provides%20the%20motivation%20for%20regular%20breaks.%0A%3E*%20At%20the%20scale%20of%20a%20day%2C%20commitments%20after%20regular%20work%20hours%20can%20help%20you%20to%20stop%20when%20you%20need%20sleep%20before%20progress.%0A%3E*%20At%20the%20scale%20of%20a%20week%2C%20weekend%20commitments%20help%20get%20your%20conscious%2C%20energysucking%20thoughts%20off%20work.%20%28My%20wife%20swears%20I%20get%20my%20best%20ideas%20on%20Friday%20evening.%29%0A%3E*%20At%20the%20scale%20of%20a%20year%2C%20mandatory%20vacation%20policies%20help%20you%20refresh%20yourself%20completely.%20The%20French%20do%20this%20right%u2014two%20contiguous%20weeks%20of%20vacation%20aren%27t%20enough.%20You%20spend%20the%20first%20week%20decompressing%2C%20and%20the%20second%20week%20getting%20ready%20to%20go%20back%20to%20work.%20Therefore%2C%20three%20weeks%2C%20or%20better%20four%2C%20are%20necessary%20for%20you%20to%20be%20your%20most%20effective%20the%20rest%20of%20the%20year.%0A%23%23%23Do%20Over%0A%3EWhat%20do%20you%20do%20when%20you%20are%20feeling%20lost%3F%20Throw%20away%20the%20code%20and%20start%20over.%0A%u4F5C%u8005%u5728%u8FD9%u4E00%u7AE0%u544A%u8BC9%u4F60%uFF0C%u4E0D%u8981%u5728%u6CE5%u6F6D%u8D8A%u9677%u8D8A%u6DF1%uFF0C%u4E0D%u8981%u5BB3%u6015%u91CD%u65B0%u5F00%u59CB%u3002%u6362%u4E2A%u4F19%u4F34%uFF0C%u6362%u4E2A%u601D%u8DEF%uFF0C%u4F60%u53EF%u80FD%u6709%u66F4%u597D%u7684%u53D1%u73B0%u3002%0A%23%23Chapter%2027.%20Testing%20Patterns%0A%23%23%23Child%20Test%0A%u8FD9%u91CC%u8BF4%u7684%u662F%uFF0C%u5982%u679C%u4F60%u89C9%u5F97%u8FD9%u4E2A%u6D4B%u8BD5%u592A%u5927%uFF0C%u90A3%u5C31%u8981%u628A%u8FD9%u4E2A%u5927%u7684%u6D4B%u8BD5%u7834%u6210%u82E5%u5E72%u4E2A%u5C0F%u7684%u6D4B%u8BD5%uFF0C%u7136%u540E%u770B%u770B%u6709%u6CA1%u6709%u8FDB%u5C55%u3002%0A%23%23%23Mock%20Object%0A%3EHow%20do%20you%20test%20an%20object%20that%20relies%20on%20an%20expensive%20or%20complicated%20resource%3F%20Create%20a%20fake%20version%20of%20the%20resource%20that%20answers%20constants.%0A%3EIf%20you%20want%20to%20use%20Mock%20Objects%2C%20you%20can%27t%20easily%20store%20expensive%20resources%20in%20global%20variables.%u8981%u8BB0%u5F97%u6E05%u7406%u73B0%u573A%uFF0C%u5426%u5219%u4E07%u4E00%u5176%u4ED6%u7684%u4EE3%u7801%u628A%u4ED6%u5F53%u4F5C%u771F%u7684object%u5728%u4F7F%u7528%uFF0C%u5C31%u4F1A%u51FA%u5927%u95EE%u9898%u3002%u6240%u4EE5%u7F16%u7801%u6700%u57FA%u672C%u7684%u51C6%u5219%u662F%u5C11%u7528%u5168%u5C40%u53D8%u91CF%u3002%0A%3EMock%20Objects%20encourage%20you%20down%20the%20path%20of%20carefully%20considering%20the%20visibility%20of%20every%20object%2C%20reducing%20the%20coupling%20in%20your%20designs.%20%u8FD9%u91CC%u8BF4%u7684%u662F%uFF0C%u5982%u679C%u4F60%u7528Mock%20Object%uFF0C%u4F60%u5C31%u8981%u601D%u8003%u6211%u5230%u5E95%u8BE5%u4E0D%u8BE5%u628A%u8FD9%u4E2A%u5E9E%u7136%u5927%u7269%u66B4%u9732%u7ED9%u8FD9%u6BB5%u4EE3%u7801%u3002%u8FD9%u79CD%u601D%u8003%u6709%u52A9%u4E8E%u51CF%u5C0F%u8026%u5408%u6027%u3002%0A%23%23%23Self%20Shunt%0A%3EHow%20do%20you%20test%20that%20one%20object%20communicates%20correctly%20with%20another%3F%20Have%20the%20object%20under%20test%20communicate%20with%20the%20test%20case%20instead%20of%20with%20the%20object%20it%20expects.%0A%0A**Without%20Self%20Shunt**%0A%60%60%60python%0A%23%20ResultListenerTest%0Adef%20testNotification%28self%29%3A%20%0A%09result%3D%20TestResult%28%29%20%0A%09listener%3D%20ResultListener%28%29%20%0A%09result.addListener%28listener%29%20%0A%09WasRun%28%22testMethod%22%29.run%28result%29%0A%09assert%201%20%3D%3D%20listener.count%20%0A%0A%23%20ResultListener%0Aclass%20ResultListener%3A%0A%09def%20__init__%28self%29%3A%20%0A%09%09self.count%3D%200%20%0A%09def%20startTest%28self%29%3A%20%0A%09%09self.count%3D%20self.count%20+%201%20%0A%60%60%60%0A%0A**With%20Self%20Shunt**%0A%60%60%60python%0A%23%20ResultListenerTest%0Adef%20testNotification%28self%29%3A%20%0A%09self.count%3D%200%20%0A%09result%3D%20TestResult%28%29%0A%09result.addListener%28self%29%0A%09WasRun%28%22testMethod%22%29.run%28result%29%20%0A%09assert%201%20%3D%3D%20self.count%0Adef%20startTest%28self%29%3A%0A%09self.count%3D%20self.count%20+%201%0A%60%60%60%0A%0A%u8FD9%u91CC%u4E3E%u5F97%u4F8B%u5B50%u4E0D%u662F%u5F88%u660E%u767D%u3002%u4E2D%u6587%u8BD1%u672C%u91CC%uFF0C%u5C06self%20shunt%u7FFB%u8BD1%u6210%u81EA%u5206%u6D41%uFF0C%u611F%u89C9%u4E5F%u662F%u5B57%u9762%u7FFB%u8BD1%u3002%u6211%u7406%u89E3%u8FD9%u91CC%u60F3%u8BF4%u7684%u662F%uFF0C%u4E0D%u4E00%u5B9A%u8981%u5B9A%u4E49%u4E00%u4E2A%u7C7B%u6765%u505A%u5F53%u524D%u88AB%u6D4B%u8BD5%u7C7B%u6765%u505A%u7684%u4E8B%u60C5%uFF0C%u53EF%u4EE5%u5728test%20case%u91CC%u9762%u7528%u8BE5%u7C7B%u66B4%u9732%u7684%u63A5%u53E3%u6765%u505A%u4E8B%u60C5%u3002%0A%3ESelf%20Shunt%20may%20require%20that%20you%20use%20**Extract%20Interface**%20to%20get%20an%20interface%20to%20implement.%20You%20will%20have%20to%20decide%20whether%20extracting%20the%20interface%20is%20easier%2C%20or%20if%20testing%20the%20existing%20class%20as%20a%20**black%20box**%20is%20easier.%0A%0A%u8FD9%u91CC%u53C8%u770B%u6655%u4E86%uFF0Cself%20shunt%u8DDFextract%20interface%u6709%u5565%u5173%u7CFB%u3002%0A%3E%u5F53%u6211%u4EEC%u6CE8%u610F%u5230%u5728%u6211%u4EEC%u4EE3%u7801%u7ED3%u6784%u4E2D%u8D85%u8FC7%u4E00%u4E2A%u7C7B%u4F7F%u7528%u5230%u53E6%u4E00%u4E2A%u7279%u6B8A%u7C7B%u4E2D%u7684%u540C%u4E00%u4E2A%u65B9%u6CD5%u65F6%uFF0C%u6B64%u65F6%u5E94%u8BE5%u5C06%u8BE5%u7C7B%u4E2D%u7684%u65B9%u6CD5%u63D0%u53D6%u51FA%u6765%u653E%u5230%u4E00%u4E2A%u63A5%u53E3%uFF08interface%uFF09%u4E2D%uFF0C%u5E76%u5BF9%u5916%u63D0%u4F9B%u8FD9%u4E2A%u63A5%u53E3%u4EE5%u4FBF%u88AB%u7528%u6237%u7C7B%u8BBF%u95EE%u3001%u4F7F%u7528%uFF0C%u8FD9%u6837%u6709%u5229%u4E8E%u6253%u7834%u539F%u6765%u591A%u4E2A%u7C7B%u4E0E%u8FD9%u4E2A%u7279%u6B8A%u7C7B%u7684%u4F9D%u8D56%u5173%u7CFB%u3002%u8FD9%u4E2A%u91CD%u6784%u624B%u6CD5%u5F88%u5BB9%u6613%u5B9E%u73B0%uFF0C%u66F4%u91CD%u8981%u7684%u662F%u8FD9%u6837%u505A%u6709%u5229%u4E8E%u677E%u8026%u5408%u3002%20%u2014%u2014%u7F51%u7EDC%u4E0A%u7684%u89E3%u91CA%0A%0A%u6211%u89C9%u5F97%u8FD9%u91CC%u4F5C%u8005%u63D0%u5230%u7684extract%20interface%uFF0C%u5E76%u4E0D%u662F%u6211%u4EEC%u8BF4%u7684%u91CD%u6784%u91CC%u7684extract%20interface%u3002%u8FD9%u91CC%u8BF4%u8981%u6D4B%u8BD5A%u7C7B%u548CB%u7C7B%u901A%u4FE1%uFF0C%u6211%u4EEC%u4E0D%u76F4%u63A5%u7528B%u7C7B%uFF0C%u800C%u7528test%20case%u4E0EA%u7C7B%u901A%u4FE1%uFF0C%u4EE5%u6D4B%u8BD5%u901A%u4FE1%u7684A%u7C7B%u7AEF%u63A5%u53E3%u662F%u5426%u6B63%u786E%u3002extract%20interface%u8BF4%u7684%u662F%u4ECEB%u7C7B%u4E2Dextract%u63A5%u53E3%u51FA%u6765%u5230test%20case%u4E2D%u53BBimplement%u3002%u6216%u8005%u628AB%u7C7B%u76F4%u63A5%u5F53%u9ED1%u76D2%u5B50%u5728test%20case%u4E2D%u4F7F%u7528%u3002%0A%23%23%23Log%20String%0A%u8FD9%u4E2A%u5F88%u6709%u7528%u3002%u6709%u4E9B%u51FD%u6570%u6216%u8005%u8FC7%u7A0B%u662F%u6CA1%u6709%u8FD4%u56DE%u503C%u7684%uFF0C%u6211%u4EEC%u5C31%u53EF%u4EE5%u7528Log%20String%u7684%u65B9%u5F0F%u6765%u67E5%u770B%u8BE5%u8FC7%u7A0B%u6216%u51FD%u6570%u6709%u6CA1%u6709%u88AB%u8C03%u7528%u3002%u7C7B%u4F3C%u6211%u4EEC%u770Btrace%20log%u4E00%u6837%uFF0C%u901A%u8FC7%u770Btrace%20log%u80FD%u77E5%u9053%u673A%u5668%u6267%u884C%u7684%u4EE3%u7801%u662F%u5426%u6B63%u786E%u3002%0A%23%23%23Crash%20Test%20Dummy%0A%3EHow%20do%20you%20test%20error%20code%20that%20is%20unlikely%20to%20be%20invoked%3F%20Invoke%20it%20anyway%20with%20a%20special%20object%20that%20throws%20an%20exception%20instead%20of%20doing%20real%20work.%0A%23%23%23Broken%20Test%0A%u4F5C%u8005%u7ECF%u5E38%u80FD%u7ED9%u51FA%u4E00%u4E9B%u5F88%u5B9E%u7528%u7684%u5C0F%u7ECF%u9A8C%uFF0C%u8FD9%u91CC%u53C8%u6765%u4E86%uFF0C%u770B%u4E0B%u9762%u3002%0A%3EHow%20do%20you%20leave%20a%20programming%20session%20when%20you%27re%20programming%20alone%3F%20Leave%20the%20last%20test%20broken.%0A%0A%u5F53%u4F60%u8981%u8F6C%u53BB%u505A%u53E6%u5916%u4E00%u4EF6%u4E8B%u60C5%uFF0C%u800C%u624B%u5934%u4E0A%u7684%u4E8B%u60C5%u53C8%u6CA1%u7ED3%u675F%u65F6%u600E%u4E48%u529E%uFF1F%u8BA9%u6700%u540E%u4E00%u4E2A%u6D4B%u8BD5%u6545%u610F%u7684%u4E0D%u901A%u8FC7%u3002%u8FD9%u6837%u4E0B%u6B21%u5C31%u77E5%u9053%u4ECE%u54EA%u513F%u5F00%u59CB%uFF0C%u7136%u540E%u80FD%u8FC5%u901F%u4E0A%u624B%u3002%u918D%u9190%u704C%u9876%u554A%uFF01%u4EE5%u524D%u90FD%u662F%u8BA4%u4E3A%u505A%u53E6%u5916%u4E00%u4EF6%u4E8B%u60C5%u4E4B%u524D%u8981%u628A%u624B%u5934%u4E0A%u7684%u4E8B%u60C5%u5F04%u7684%u5C3D%u5584%u5C3D%u7F8E%uFF0C%u5176%u5B9E%u4E0D%u7136%u3002%u600E%u4E48%u505A%u5229%u4E8E%u540E%u671F%u7EE7%u7EED%u8FDB%u884C%uFF0C%u90A3%u5C31%u5E94%u8BE5%u600E%u4E48%u505A%u3002%0A%23%23%23Clean%20Check-in%0A%3EYou%20will%20occasionally%20find%20a%20test%20broken%20in%20the%20integration%20suite%20when%20you%20try%20to%20check%20in.%20What%20to%20do%3F%0A**The%20simplest%20rule**%20is%20to%20just%20throw%20away%20your%20work%20and%20start%20over.%20The%20broken%20test%20is%20pretty%20strong%20evidence%20that%20you%20didn%27t%20know%20enough%20to%20program%20what%20you%20just%20programmed.%0A%u4EC0%u4E48%u4EC0%u4E48%uFF1F%u7ADF%u7136%u8981%u6211%u4EEC%u6254%u6389%u6211%u4EEC%u505A%u7684%uFF0C%u7136%u540E%u91CD%u65B0%u5F00%u59CB%u3002%u5C31%u56E0%u4E3A%u6211%u4EEC%u7684%u6D4B%u8BD5%u548Cintegration%u6D4B%u8BD5%u7ED3%u679C%u4E0D%u4E00%u81F4%u3002%0A%u5927%u5E08%u7684%u8BDD%u603B%u662F%u6709%u9053%u7406%u7684%uFF0C%u4EE5%u540E%u8981%u597D%u597D%u8003%u8651%u8FD9%u4E00%u70B9%u3002%0A%23%23Chapter%2028.%20Green%20Bar%20Patterns%0A%23%23%23Fake%20It%20%28%27Til%20You%20Make%20It%29%0A%u505A%u5047%u4EE3%u7801%u8BA9%u6D4B%u8BD5%u901A%u8FC7%uFF0C%u7136%u540E%u518D%u4E00%u9879%u4E00%u9879%u5730%u5B8C%u6210%0A%23%23%23Triangulate%0A%u4F5C%u8005%u63D0%u5230%u7684%u6240%u8C13%u4E09%u89D2%u6CD5%u3002%u901A%u4FD7%u7684%u8BF4%uFF0C%u5C31%u662F%u53EA%u5728%u5FC5%u8981%u7684%u65F6%u5019%u505A%u62BD%u8C61%u6216%u901A%u7528%u5316%u5904%u7406%u3002%u901A%u5E38%u662F%u5728TDD%u7684%u91CD%u6784%u8FD9%u4E00%u6B65%u5B8C%u6210%u8FD9%u4E2A%u62BD%u8C61%u7684%u8FC7%u7A0B%u3002%u518D%u901A%u4FD7%u70B9%u8BF4%uFF0C%u5C31%u662F%u591A%u5F04%u51E0%u4E2Acase%uFF0C%u518D%u603B%u7ED3%u5171%u540C%u70B9%uFF0C%u4E4B%u540E%u505A%u62BD%u8C61%0A%u4ECE%u53E6%u4E00%u4E2A%u89D2%u5EA6%uFF0C%u4F5C%u8005%u53C8%u8BF4%u4E86%uFF1A%0A%3EI%20only%20use%20Triangulation%20when%20I%27m%20really%2C%20really%20unsure%20about%20the%20correct%20abstraction%20for%20the%20calculation.%20Otherwise%20I%20rely%20on%20either%20Obvious%20Implementation%20or%20Fake%20It.%0A%23%23%23One%20to%20Many%0A%3EHow%20do%20you%20implement%20an%20operation%20that%20works%20with%20collections%20of%20objects%3F%20Implement%20it%20without%20the%20collections%20first%2C%20then%20make%20it%20work%20with%20collections.%0A%0A%u4E2D%u5FC3%u601D%u60F3%u8FD8%u662F%u4E00%u4E2A%uFF0C%u4ECE%u663E%u800C%u6613%u89C1%u7684%u5730%u65B9%u5F00%u59CB%uFF0C%u6BCF%u6B21%u53EA%u505A%u4E00%u5C0F%u6B65%uFF0C%u7136%u540E%u4E00%u6B65%u4E00%u6B65%u5230%u8FBE%u6700%u7EC8%u7684%u76EE%u7684%u5730%u3002%u770B%u4E0B%u9762%u7684%u4F8B%u5B50%uFF1A%0A%60%60%60java%0A//%201.%20fake%20code%0Apublic%20void%20testSum%28%29%20%7B%20%0A%09assertEquals%285%2C%20sum%285%29%29%3B%20%0A%7D%20%0Aprivate%20int%20sum%28int%20value%29%20%7B%0A%09return%20value%3B%0A%7D%0A%0A//%202.%20add%20arrays%0Apublic%20void%20testSum%28%29%20%7B%0A%09assertEquals%285%2C%20sum%285%2C%20new%20int%5B%5D%20%7B5%7D%29%29%3B%0A%7D%0Aprivate%20int%20sum%28int%20value%2C%20int%5B%5D%20values%29%20%7B%0A%09return%20value%3B%0A%7D%0A%0A//%203.%20add%20real%20sum%20functionality%0Apublic%20void%20testSum%28%29%20%7B%0A%09assertEquals%285%2C%20sum%285%2C%20new%20int%5B%5D%20%7B5%7D%29%29%3B%0A%7D%0Aprivate%20int%20sum%28int%20value%2C%20int%5B%5D%20values%29%20%7B%0A%09int%20sum%3D%200%3B%20%0A%09for%20%28%20int%20i%3D%200%3B%20i%3Cvalues.length%3B%20i++%29%0A%09%09sum%20+%3D%20values%5Bi%5D%3B%0A%09return%20sum%3B%0A%7D%0A%0A//%204.%20delete%20unnecessary%20code%0Apublic%20void%20testSum%28%29%20%7B%20%0A%09assertEquals%285%2C%20sum%28new%20int%5B%5D%20%7B5%7D%29%29%3B%0A%7D%0Aprivate%20int%20sum%28%20int%5B%5D%20values%29%20%7B%0A%09int%20sum%3D%200%3B%20%0A%09for%20%28int%20i%3D%200%3B%20i%3Cvalues.length%3B%20i++%29%0A%09%09sum%20+%3D%20values%5Bi%5D%3B%0A%09return%20sum%3B%20sum%3B%0A%7D%0A%60%60%60%0A%u7EC6%u7EC6%u4F53%u4F1A%u5176%u95F4%u7684%u5DEE%u522B%u5427%u3002%0A%23%23Chapter%2029.%20xUnit%20Patterns%0A%23%23%23Assertion%0A%3EHow%20do%20you%20check%20that%20tests%20worked%20correctly%3F%20%0A%3EWrite%20boolean%20expressions%20that%20automate%20your%20judgment%20about%20whether%20the%20code%20worked.%0A%0A%u4F5C%u8005%u5728%u8FD9%u4E00%u7AE0%u53C8%u7ED9%u51FA%u4E86%u4E00%u4E2A%u5F88%u6709%u6307%u5BFC%u610F%u4E49%u7684%u89C2%u70B9%u3002%0A%3E%u5C3D%u91CF%u4E0D%u8981%u505A%u767D%u76D2%u6D4B%u8BD5%uFF0C%u5982%u679C%u4F60%u60F3%u505A%uFF0C%u8BF4%u660E%u4F60%u7684%u8BBE%u8BA1%u6709%u95EE%u9898%0A%23%23%23Fixture%0A%u8FD9%u91CC%u4F5C%u8005%u629B%u51FA%u4E86%u4E00%u4E2A%u95EE%u9898%uFF0C%u5982%u679C%u6709%u591A%u4E2A%u6D4B%u8BD5%u6709%u7C7B%u4F3C%u7684%u4EE3%u7801%u600E%u4E48%u529E%uFF1F%u4E24%u4E2A%u9009%u62E9%uFF1A%0A1.%20test%20case%u7C7B%u5B9A%u4E49%u4E2D%u62BD%u8C61%u51FA%u7C7B%u4F3CsetUp%u7684%u51FD%u6570%uFF0C%u6765%u505A%u91CD%u590D%u7684%u5DE5%u4F5C%0A2.%20%u6216%u8005%u7528%u7C7B%u4F3C%u4EE3%u7801%u751F%u6210%u5668%u7684%u4E1C%u897F%u6765%u4E3A%u6240%u6709%u9700%u8981%u7684%u6D4B%u8BD5%u751F%u6210fixture%0A%0A%u65B9%u6CD51%u6709%u65F6%u5019%u66F4%u7B26%u5408DRY%u539F%u5219%uFF0C%u4F46%u5B83%u7684%u7F3A%u70B9%u662F%uFF0C%u4F60%u8981%u8BB0%u4F4FsetUp%u91CC%u9762%u5230%u5E95%u505A%u4E86%u4EC0%u4E48%u3002%u4E24%u79CD%u65B9%u6CD5%u90FD%u53EF%u4EE5%uFF0C%u9009%u62E9%u66F4%u5408%u9002%u7684%u3002%0A%0A%23%23%23External%20Fixture%0A%u6240%u8C13%u7684%u5916%u90E8fixture%uFF0C%u5C31%u662F%u8981%u4FDD%u8BC1%u6D4B%u8BD5%u80FD%u6062%u590D%u73AF%u5883%u914D%u7F6E%u3002%0A%3E%u5728%u6240%u6709%u6D4B%u8BD5%u5F00%u59CB%u524D%u8C03setUp%uFF0C%u6240%u6709%u6D4B%u8BD5%u7ED3%u675F%u540E%u8C03tearDown%u3002%u8FD9%u5C31%u662FExternal%20Fixture%0A%23%23%23Test%20Method%0A%u8FD9%u4E00%u8282%u9488%u5BF9%u5982%u4F55%u5199%u6D4B%u8BD5%u65B9%u6CD5%u7ED9%u51FA%u4E86%u4E00%u4E9B%u5EFA%u8BAE%u3002%0A1.%20hierarchy%0A*%20Module%20%24%5Crightarrow%24%20Framework%0A*%20Class%20%24%5Crightarrow%24%20internal%20fixture%0A*%20Method%20%24%20%5Crightarrow%24%20test%20case%0A2.%20%u540D%u5B57%u957F%u6CA1%u5173%u7CFB%uFF0C%u8981%u8BA9%u4EE5%u540E%u7684%u8BFB%u8005%u6709%u8DB3%u591F%u7684%u7EBF%u7D22%u77E5%u9053%u4E3A%u4EC0%u4E48%u8981%u5199%u8FD9%u4E2Atest%0A3.%20%u6D4B%u8BD5%u4EE3%u7801%u8981%u591F%u76F4%u89C9%uFF0C%u8981%u591F%u7B80%u6D01%uFF08%u6700%u77ED3%u884C%uFF09%20%0A4.%20%u6CE8%u91CA%u91CC%u5199outline%uFF0C%u4E00%u9897%u661F%u8868%u793A%u4E00%u7EA7%uFF0C%u4F5C%u8005%u8BF4%u4ED6%u4E00%u822C%u51992-3%u7EA7outline%0A%60%60%60%20cpp%0A/*%20Adding%20to%20tuple%20spaces.%20*/%20%0A/*%20Taking%20from%20tuple%20spaces.%20*/%0A/**%20Taking%20a%20non-existent%20tuple.%20**/%20%0A/**%20Taking%20an%20existing%20tuple.%20**/%20%0A/**%20Taking%20multiple%20tuples.%20**/%20%0A/*%20Reading%20from%20tuple%20space.%20*/%0A%60%60%60%0A%23%23%23Exception%20Test%0A%u8FD9%u4E00%u8282%u8BF4%u7684%u662F%uFF0C%u5982%u679C%u5F85%u6D4B%u7684%u51FD%u6570%u9700%u8981%u629B%u51FA%u5F02%u5E38%uFF0C%u7528%u5982%u4E0B%u7684%u65B9%u6CD5%uFF1A%0A%60%60%60java%0Apublic%20void%20testMissingRate%28%29%20%7B%0A%09try%20%7B%0A%09%09exchange.findRate%28%22USD%22%2C%20%22GBP%22%29%3B%20%0A%09%09fail%28%29%3B%20//%20%u5982%u679C%u6CA1%u629B%u5F02%u5E38%u5C31fail%0A%09%7D%20catch%20%28IllegalArgumentException%20expected%29%20%7B%20%0A%09%7D%0A%7D%0A%60%60%60%0A%23%23%23All%20Tests%0A%3EHow%20do%20you%20run%20all%20tests%20together%3F%20Make%20a%20suite%20of%20all%20the%20suites%u2014one%20for%20each%20package%2C%20and%20one%20aggregating%20the%20package%20tests%20for%20the%20whole%20application.%0A%60%60%60java%0Apublic%20class%20AllTests%20%7B%0A%09//%20%u8FD9%u91CC%u6709%u4E2Amain%uFF0C%u65B9%u4FBF%u76F4%u63A5%u8C03%u7528%u7A0B%u5E8F%u5F00%u59CB%u6D4B%u8BD5%0A%09public%20static%20void%20main%28String%5B%5D%20args%29%20%7B%20%0A%09%09junit.swingui.TestRunner.run%28AllTests.class%29%3B%20%0A%09%7D%0A%09//%20%u8FD9%u91CC%u6709%u4E2Asuite%u5305%u542B%u4E86%u6240%u6709test%20case%0A%09public%20static%20Test%20suite%28%29%20%7B%20%0A%09%09TestSuite%20result%3D%20new%20TestSuite%28%22TFD%20tests%22%29%3B%20%0A%09%09result.addTestSuite%28MoneyTest.class%29%3B%20%0A%09%09result.addTestSuite%28ExchangeTest.class%29%3B%20%0A%09%09result.addTestSuite%28IdentityRateTest.class%29%3B%20%0A%09%09return%20result%3B%0A%09%7D%0A%7D%0A%60%60%60%0A%23%23Chapter%2030.%20Design%20Patterns%0A%u4F5C%u8005%u5728%u8FD9%u4E00%u7AE0%u5F15%u5165%u4E86%u4E00%u4E9BTDD%u4E2D%u4F1A%u7528%u5230%u7684%u8BBE%u8BA1%u6A21%u5F0F%u3002%u6709%u4E9B%u662F%u5199%u6D4B%u8BD5%u65F6%u7528%u7684%uFF0C%u6709%u7684%u662F%u91CD%u6784%u65F6%u7528%u7684%uFF0C%u6709%u7684%u4E24%u8005%u7686%u53EF%u3002%u4E0B%u9762%u662F%u672C%u7AE0%u6240%u6709%u6A21%u5F0F%u7684%u603B%u7EB2%u3002%0A%21%5BAlt%20text%5D%28./1456036251528.png%29%0A%23%23%23Command%0A%3EWhat%20do%20you%20do%20when%20you%20need%20the%20invocation%20of%20a%20computation%20to%20be%20more%20complicated%20than%20a%20simple%20method%20call%3F%0A%3EMake%20an%20object%20for%20the%20computation%20and%20invoke%20it.%0A%0A%u8FD9%u91CC%u6240%u8BF4%u7684%u547D%u4EE4%u662F%u8BF4%uFF0C%u628A%u8981%u8C03%u7528%u7684%u65B9%u6CD5%u5C01%u88C5%u6210%u4E00%u4E2Aobject%uFF0C%u7136%u540E%u8C03%u7528%u90A3%u4E2Aobject%0A%0A%23%23%23Value%20Object%0A%u628A%u4E00%u4E9B%u5E38%u91CF%u53C2%u6570%u505A%u6210object%u6765%u4F20%u9012%0A%3EWhen%20implementing%20a%20Value%20Object%2C%20every%20operation%20has%20to%20return%20a%20fresh%20object%2C%20%0A%3EAll%20Value%20Objects%20have%20to%20implement%20equality%20%0A%0A%23%23%23Null%20Object%0A%60%60%60java%0Apublic%20boolean%20setReadOnly%28%29%20%7B%20%0A%09SecurityManager%20guard%20%3D%20System.getSecurityManager%28%29%3B%20%0A%09if%20%28guard%20%21%3D%20null%29%20%7B%20%29%20%7B%0A%09%09guard.canWrite%28path%29%3B%0A%09%7D%20%0A%09return%20fileSystem.setReadOnly%28this%29%3B%0A%7D%0A%60%60%60%0A%u4E0A%u9762%u8FD9%u4E2A%u4F8B%u5B50getSecurityManager%u5728%u4E0D%u6210%u529F%u65F6%u8FD4%u56DENull%uFF0C%u7136%u540E%u8C03%u7528%u8005%u518D%u5224%u65AD%u8FD4%u56DE%u503C%u4EE5%u51B3%u5B9A%u4F1A%u4E0D%u4F1A%u7528%u7A7A%u6307%u9488%u6765%u8C03%u7528%u6210%u5458%u53D8%u91CF%u3002%0A%u8FD9%u662F%u4E00%u4E2A%u5F88%u5E38%u89C1%u7684%u505A%u6CD5%uFF0C%u8FD9%u6837%u505A%u4E0D%u597D%u7684%u662F%uFF0C%u901A%u5E38%u8FD9%u79CD%u5224%u65AD%u4F1A%u975E%u5E38%u975E%u5E38%u591A%uFF0C%u800C%u4E14%u7ED9%u4EBA%u4E00%u79CD%u201C%u4F1A%u4E0D%u4F1A%u6709%u54EA%u91CC%u6CA1%u5224%u65AD%uFF0C%u4EE5%u540E%u4F1A%u5BFC%u81F4crash%uFF1F%u201D%u7684%u62C5%u5FE7%u3002%0ANull%20Object%u8BBE%u8BA1%u6A21%u5F0F%u5C31%u662F%u8BA9getSecurityManager%u8FD4%u56DE%u4E00%u4E2A%u7279%u6B8A%u7684object%u6765%u53D6%u4EE3Null%uFF0C%u8FD9%u6837%u8BA9%u6240%u6709%u7684%u64CD%u4F5C%u90FD%u7EDF%u4E00%uFF0C%u4EE5%u51CF%u5C11%u91CD%u590D%u4EE3%u7801%u3002%0A%0A%23%23%23Template%20Method%0A%u6A21%u677F%u65B9%u6CD5%u3002%0A%3EWhen%20you%20find%20two%20variants%20of%20a%20sequence%20in%20two%20subclasses%2C%20you%20need%20to%20gradually%20move%20them%20closer%20together.%20Once%20you%27ve%20extracted%20the%20parts%20that%20are%20different%20from%20other%20methods%2C%20what%20you%20are%20left%20with%20is%20the%20Template%20Method.%20%0A%60%60%60java%0A//%20TestCase%0Apublic%20void%20runBare%28%29%20throws%20Throwable%20%7B%20%0A%09setUp%28%29%3B%20%0A%09try%20%7B%20%0A%09%09runTest%28%29%3B%0A%09%7D%0A%09finally%20%7B%0A%09%09tearDown%28%29%3B%0A%09%7D%0A%7D%0A%60%60%60%0A%0A%23%23%23Pluggable%20Object%0A%u4E2D%u6587%u8BD1%u540D%u4E3A%u63D2%u5165%u5F0F%u5BF9%u8C61%u3002%u5F88%u96BE%u7406%u89E3%u54EA%u91CC%u6709%u8FD9%u4E2A%u6240%u8C13%u7684%u201C%u63D2%u5165%u201D%u3002%u8FD9%u4E2A%u8BBE%u8BA1%u6A21%u5F0F%u6838%u5FC3%u7684%u601D%u60F3%u5C31%u662F%u6D88%u9664%60if...else...%60%u7684%u60C5%u51B5%u3002%0A%u770B%u4F5C%u8005%u7684%u4F8B%u5B50%uFF0C%u89E3%u91CA%u7684%u5F88%u6E05%u695A%uFF1A%0A%60%60%60java%0A//%20Without%20Pluggable%20Object%0AFigure%20selected%3B%20%0Apublic%20void%20mouseDown%28%29%20%7B%20%0A%09selected%3D%20findFigure%28%29%3B%0A%09if%20%28selected%20%21%3D%20null%29%20//%20%u5230%u5904%u662Fnull%u5224%u65AD%0A%09%09select%28selected%29%3B%20%0A%7D%0Apublic%20void%20mouseMove%28%29%20%7B%0A%09if%20%28selected%20%21%3D%20null%29%0A%09%09move%28selected%29%3B%20%0A%09else%0A%09%09moveSelectionRectangle%28%29%3B%20%0A%7D%0Apublic%20void%20mouseUp%28%29%20%7B%0A%09if%20%28selected%20%3D%3D%20null%29%0A%09%09selectAll%28%29%3B%0A%7D%20%0A%0A//%20With%20Pluggable%20Object%0ASelectionMode%20mode%3B%0Apublic%20void%20mouseDown%28%29%20%7B%0A%09selected%3D%20findFigure%28%29%3B%0A%09//%20%u5168%u90E8%u5F52%u7ED3%u5230mode%u5B50%u7C7B%u91CC%u9762%u53BB%u5904%u7406%0A%09if%20%28selected%20%21%3D%20null%29%0A%09%09mode%3D%20SingleSelection%28selected%29%3B%0A%09else%0A%09%09mode%3D%20MultipleSelection%28%29%3B%0A%7D%0Apublic%20void%20mouseMove%28%29%20%7B%0A%09mode.mouseMove%28%29%3B%0A%7D%0Apublic%20void%20mouseUp%28%29%20%7B%0A%09mode.mouseUp%28%29%3B%0A%7D%0A%60%60%60%0A%23%23%23Pluggable%20Selector%0A%u8FD9%u4E00%u8282%u7684%u8BBE%u8BA1%u6A21%u5F0F%u548C%u4E0A%u4E00%u8282%u7684%u6781%u4E3A%u7C7B%u4F3C%u3002%u8FD9%u6B21%u8981%u5904%u7406%u6389%u7684%u662F%60switch...case...%60%u800C%u53C8%u4E0D%u7528%u5B50%u7C7B%u7EE7%u627F%u7684%u65B9%u5F0F%u6765%u52A8%u6001%u8C03%u7528%u3002%u8FD9%u91CC%u4F7F%u7528%u7684%u65B9%u6CD5%u53EB%u53CD%u5C04%u3002%0A%3E%u53CD%u5C04%uFF1A%u901A%u8FC7%u81EA%u8EAB%u7684%u5C5E%u6027%uFF08%u5982%u7C7B%u540D%uFF0C%u65B9%u6CD5%u540D%uFF09%u6765%u505A%u76F8%u5173%u903B%u8F91%0A%0A%u540C%u6837%u770B%u4F5C%u8005%u7684%u4F8B%u5B50%0A%60%60%60java%0Avoid%20print%28%29%20%7B%20%0A%09Method%20runMethod%3D%20getClass%28%29.getMethod%28printMessage%2C%20null%29%3B%0A%09runMethod.invoke%28this%2C%20new%20Class%5B0%5D%29%3B%0A%7D%0A%60%60%60%0A%u770B%u5230%u4E86%u6CA1getClass%uFF0CgetMethod%uFF0C%u8FD9%u5C31%u662F%u53CD%u5C04%u3002%u6709%u4E86%u8FD9%u4E2A%u51FD%u6570%u65E2%u4E0D%u9700%u8981switch%u6765%u8C03%u7528%u4E0D%u540C%u7684printxxx%u51FD%u6570%28printHtml%2C%20printXml...%29%uFF0C%u4E5F%u4E0D%u9700%u8981%u5B9A%u4E49%u5B50%u7C7B%u6765%u8C03%u7528%u4E0D%u540C%u7684print%u51FD%u6570%u3002%0A%u4F5C%u8005%u5728%u672C%u8282%u6700%u540E%u7ED9%u51FA%u4E86%u4E00%u6BB5%u5FE0%u544A%uFF1A%0A%3EPluggable%20Selector%20can%20definitely%20be%20overused.%20The%20biggest%20problem%20with%20it%20is%20tracing%20code%20to%20see%20whether%20a%20method%20is%20invoked.%20Use%20Pluggable%20Selector%20only%20when%20you%20are%20cleaning%20up%20a%20fairly%20straightforward%20situation%20in%20which%20each%20of%20a%20bunch%20of%20**subclasses%20has%20only%20one%20method.**%0A%23%23%23Factory%20Method%0A%u4F5C%u8005%u8BF4%u5DE5%u5382%u65B9%u6CD5%u63D0%u4F9B%u4E86%u7075%u6D3B%u6027%u3002%u6211%u662F%u6CA1%u770B%u51FA%u6765%u3002%u5BF9%u6BD4%u4E0B%u9762%u4E24%u6BB5%u4EE3%u7801%uFF0C%u8FD9%u91CC%u589E%u52A0%u4E86%u7075%u6D3B%u6027%uFF1F%0A%60%60%60java%0Apublic%20void%20testMultiplication%28%29%20%7B%0A%09Dollar%20five%3D%20Dollar%20five%3D%20new%20Dollar%285%29%3B%0A%09assertEquals%28new%20Dollar%2810%29%2C%20five.times%282%29%29%3B%20%0A%09assertEquals%28new%20Dollar%2815%29%2C%20five.times%283%29%29%3B%0A%7D%0A%0Apublic%20void%20testMultiplication%28%29%20%7B%20%0A%09Dollar%20five%20%3D%20Money.dollar%285%29%3B%20//%20%u5DE5%u5382%u65B9%u6CD5%0A%09assertEquals%28new%20Dollar%2810%29%2C%20five.times%282%29%29%3B%20%0A%09assertEquals%28new%20Dollar%2815%29%2C%20five.times%283%29%29%3B%20%0A%7D%0A//%20Money%0Astatic%20Dollar%20dollar%28%20Dollar%20dollar%28int%20amount%29%20%7B%20%0A%09return%20new%20Dollar%28amount%29%3B%0A%7D%0A%60%60%60%0A%u4F5C%u8005%u6700%u540E%u7ED9%u51FA%u4E86%u4E00%u70B9%u5FE0%u544A%0A%3E%20You%20have%20to%20remember%20that%20the%20method%20is%20really%20creating%20an%20object%2C%20even%20though%20it%20doesn%27t%20look%20like%20a%20constructor.%20Use%20Factory%20Method%20only%20when%20you%20need%20the%20flexibility%20it%20creates.%20%0A%0A%23%23%23Imposter%0A%u4E2D%u6587%u8BD1%u540D%u4E3A%u5192%u540D%u9876%u66FF%u3002%u6839%u636E%u6587%u4E2D%u7684%u9610%u8FF0%uFF0C%u6211%u7406%u89E3%u8FD9%u91CC%u8BB2%u7684%u4E0D%u662F%u4E00%u79CD**%u8BBE%u8BA1**%u6A21%u5F0F%uFF0C%u800C%u53EA%u662F%u4E00%u79CD%u6A21%u5F0F%uFF0C%u4E00%u79CD%u7279%u6B8A%u60C5%u51B5%u3002%u6307%u7684%u662F%u5982%u679C%u67D0%u4E00%u6BB5%u4EE3%u7801%uFF0C%u53EA%u5C06%u5176%u4E2D%u7684%u4E00%u4E2A%u540D%u5B57%uFF08%u7C7B%u3001%u65B9%u6CD5%u6216%u8005%u53D8%u91CF%u7B49%u7B49%uFF09%u66FF%u6362%u6210%u53E6%u5916%u4E00%u4E2A%u540D%u5B57%uFF0C%u5C31%u80FD%u5F97%u5230%u4E00%u6BB5%u65B0%u7684%u4EE3%u7801%uFF0C%u79F0%u4E3A%u5192%u540D%u9876%u66FF%u3002%u53D1%u73B0%u53EF%u4EE5Imposter%u7684%u60C5%u51B5%uFF0C%u5176%u5B9E%u5C31%u662F%u53D1%u73B0%u91CD%u590D%u3002%0A%60%60%60java%0A//%20%u4E0B%u9762%u8FD9%u4E24%u6BB5%u4EE3%u7801%u5C31%u53EA%u662F%u5C06RectangleFigure%u6362%u6210OvalFigure%uFF0C%u6B64%u662F%u4E3AImposter%u5192%u540D%u9876%u66FF%u3002%0AtestRectangle%28%29%20%7B%0A%09Drawing%20d%3D%20new%20Drawing%28%29%3B%0A%09d.addFigure%28new%20RectangleFigure%280%2C%2010%2C%2050%2C%20100%29%29%3B%20RectangleFigure%280%2C%2010%2C%2050%2C%20100%29%29%3B%0A%09RecordingMedium%20brush%3D%20new%20RecordingMedium%28%29%3B%20%0A%09d.display%28brush%29%3B%20%0A%09assertEquals%28%22rectangle%200%2010%2050%20100%5Cn%22%2C%20brush.log%28%29%29%3B%0A%7D%0A%0AtestOval%28%29%20%7B%0A%09Drawing%20d%3D%20new%20Drawing%28%29%3B%0A%09d.addFigure%28new%20OvalFigure%280%2C%2010%2C%2050%2C%20100%29%29%3B%20%0A%09RecordingMedium%20brush%3D%20new%20RecordingMedium%28%29%3B%20%0A%09d.display%28brush%29%3B%0A%09assertEquals%28%22oval%200%2010%2050%20100%5Cn%22%2C%20brush.log%28%29%29%3B%20%0A%7D%0A%60%60%60%0A%u4F5C%u8005%u8FD8%u63D0%u5230%u53E6%u5916%u4E24%u4E2A%u5728%u91CD%u6784%u4E2D%u7ECF%u5E38%u7528%u5230%u7684%u8BBE%u8BA1%u6A21%u5F0F%uFF0C%u672C%u8D28%u5176%u5B9E%u5C31%u662FImposter%20%u2014%u2014%20Null%20Object%20%26%20Composite%u3002%0A%0A%23%23%23Composite%0A%u6839%u636E%u4E0A%u4E00%u7AE0%u4F5C%u8005%u7684%u89C2%u70B9%uFF0C%u7EC4%u5408%u5176%u5B9E%u5C31%u662F%u4E00%u79CD%u5192%u540D%u9876%u66FF%u3002%u4F5C%u8005%u5728%u672C%u8282%u5F00%u5934%u8BF4%u4E86%uFF1A%0A%3EHow%20do%20you%20implement%20**an%20object**%20whose%20behavior%20is%20the%20composition%20of%20the%20behavior%20of%20a%20list%20of%20other%20objects%3F%20Make%20it%20an%20**Imposter**%20for%20**the%20component%20objects**.%0A%0A%u8BA9%u8FD9%u4E2A%u5BF9%u8C61%u79F0%u4E3A%u7EC4%u6210%u4ED6%u7684%u5BF9%u8C61%u7684%u5192%u540D%u9876%u66FF%u8005%u3002%u8BF4%u8D77%u6765%u5F88%u62D7%u53E3%u3002%u770B%u4F8B%u5B50%uFF1A%0A%60%60%60java%0A//%20Transaction%0ATransaction%28Money%20value%29%20%7B%0A%09this.value%3D%20value%3B%0A%7D%0A//%20Account%0ATransaction%20transactions%5B%5D%3B%0AMoney%20balance%28%29%20%7B%0A%09Money%20sum%3D%20Money.zero%28%29%3B%0A%09for%20%28int%20i%3D%200%3B%20i%20%3C%20transactions.length%3B%20i++%29%20%0A%09%09sum%3D%20sum.plus%28transactions%5Bi%5D.value%29%3B%0A%09return%20sum%3B%0A%7D%0A%60%60%60%0A%u8FD9%u4E2A%u5199%u6CD5%u7684%u7F3A%u70B9%u662F%uFF0C%u5F53%u8981%u7B97OverallAccount%u65F6%uFF0C%u6CA1%u8F99%u4E86%uFF0C%u8981%u5B9A%u4E49%u4E00%u4E2A%u65B0%u7C7B%u6765%u5904%u7406%u3002%u4E00%u4E2AOverallAccount%u662F%u4E00%u7EC4Account%u3002%u4F46OverallAccount%u5176%u5B9E%u8981%u505A%u7684%u4E5F%u662F%u4E00%u79CD%u7D2F%u52A0%uFF0C%u548CAccount%u505A%u7684%u5E76%u6CA1%u6709%u4EC0%u4E48%u533A%u522B%u3002%u8FD9%u5C31%u662F%u91CD%u590D%u3002%0A%u89E3%u51B3%u7684%u529E%u6CD5%u5C31%u662F%u6240%u8C13%u7684Composite%u3002%u628A%u8981%u7D2F%u52A0%u7684%u4E1C%u897F%u62BD%u8C61%u51FA%u6765%u79F0%u4E3AHoldings%uFF0CAccount%u7D2F%u52A0%u7684%u662FHoldings%uFF0COverallAccount%u7D2F%u52A0%u7684%u4E5F%u662FHoldings%u3002%0A%60%60%60java%0AHolding%20holdings%5B%5D%3B%0AMoney%20balance%28%29%20%7B%0A%09Money%20sum%3D%20Money.zero%28%29%3B%0A%09for%20%28%20%28int%20i%3D%200%3B%20i%20%3C%20holdings.length%3B%20i++%29%0A%09sum%3D%20sum.plus%28holdings%5Bi%5D.balance%28%29%29%3B%0A%09return%20sum%3B%0A%7D%0A%60%60%60%0A%u5199%u5230%u8FD9%u91CC%uFF0C%u6211%u4ECD%u7136%u4E0D%u662F%u7279%u522B%u660E%u767D%u4F55%u4E3AComposite%uFF08%u62FC%u5408%uFF0C%u590D%u5408%u7684%u610F%u601D%uFF09%u3002%u4F46%u6211%u4EEC%u662F%u7AD9%u5728%u5DE8%u4EBA%u7684%u80A9%u8180%u4E0A%uFF0C%u5BF9%u6BD4%u9605%u8BFB%u4E86%u4E2D%u6587%u8BD1%u672C%u540E%u6709%u4E86%u4E00%u70B9%u70B9%u611F%u89C9%u3002%u8FD9%u4E2A%u8BBE%u8BA1%u6A21%u5F0F%u7684%u4E2D%u6587%u8BD1%u540D%u4E3A-**%u9012%u5F52%u7EC4%u5408**%u3002%u6240%u8C13%u9012%u5F52%uFF0C%u5E94%u5F53%u6307%u7684%u662F%uFF0C%u901A%u8FC7%u6982%u5FF5%u4E0A%u7684%u62BD%u8C61%uFF0C%u5C06%u4E00%u4E2A%u5927%u7684%u6DF7%u5408%u7C7B%u5199%u6210%u4E00%u4E9B%u66F4%u62BD%u8C61%u7684%u6982%u5FF5%u7684%u7EC4%u5408%u3002%u4F8B%u5982%3A%20%0A%3EOvreallAccount%20%3C-%20Account%20%3C-%20Transactions%0A%3EOverallAccount%20%3C-%20Holding%20%26%20Account%20%3C-Holding%0A%0A%u901A%u8FC7%u9012%u5F52%u5730%u5C06OverallAccount%u5192%u540D%u9876%u66FF%u6210Holding%uFF0C%u4ECE%u800C%u4F7FOverallAccount%u6210%u4E3A%u4E00%u4E2AAccount%u6765%u8FBE%u5230%u6D88%u9664%u91CD%u590D%u3002%0A%u4F5C%u8005%u5728%u672C%u8282%u6700%u540E%u7ED9%u51FA%u4E86%u4F55%u65F6%u4F7F%u7528Composite%u7684%u5EFA%u8BAE%uFF1A%0A%3EAs%20is%20obvious%20from%20this%20discussion%2C%20I%27m%20still%20not%20able%20to%20articulate%20how%20to%20guess%20when%20a%20collection%20of%20objects%20is%20just%20a%20collection%20of%20objects%20and%20when%20you%20really%20have%20a%20Composite.%20The%20good%20news%20is%2C%20since%20you%27re%20getting%20good%20at%20refactoring%2C%20%3Cb%3E%3Cfont%20color%3D%22IndianRed%22%3Ethe%20moment%20the%20duplication%20appears%3C/font%3E%3C/b%3E%2C%20you%20can%20introduce%20Composite%20and%20watch%20program%20complexity%20disappear.%0A%0A%23%23%23Collecting%20Parameter%0A%u770B%u4E0D%u61C2%uFF0C%u4E0D%u77E5%u6240%u4E91%0A%0A%23%23%23Singleton%0A%u8FD9%u4E2A%u5C31%u662F%u5F88%u8457%u540D%u7684%u5355%u4F8B%u6A21%u5F0F%uFF0C%u7ECF%u5E38%u4F1A%u7528%u5230%u3002%u4F5C%u8005%u6CA1%u6709%u8BE6%u52A0%u9610%u91CA%uFF0C%u53EA%u662F%u544A%u8BEB%u8BFB%u8005%u4E0D%u8981%u7528%u5168%u5C40%u53D8%u91CF%28global%20variable%29%u3002%u6211%u731C%u4F5C%u8005%u7684%u610F%u601D%u662F%uFF0C%u5982%u679C%u8981%u7528%u5168%u5C40%u53D8%u91CF%uFF0C%u5C31%u7528%u5355%u4F8B%u6A21%u5F0F%u7684%u7C7B%u5BF9%u8C61%u5427%u3002%0A%0A%23%23Chapter%2031.%20Refactoring%0A%u5728TDD%u4E2D%uFF0C%u91CD%u6784%u8981%u505A%u7684%u5C31%u662F%uFF0C%u4FDD%u8BC1%u6240%u6709%u6D4B%u8BD5%u901A%u8FC7%u7684%u60C5%u51B5%u4E0B%uFF0C%u964D%u4F4E%u91CD%u590D%uFF08code%u4E4B%u95F4%uFF0Ccode%u4E0Etest%u4E4B%u95F4%uFF09%u3002%0A%23%23%23Reconcile%20Differences%0A%3E-%20How%20do%20you%20unify%20two%20similar%20looking%20pieces%20of%20code%3F%20Gradually%20bring%20them%20closer.%20Unify%20them%20only%20when%20they%20are%20absolutely%20identical.%20%u4E0D%u8981%u5F3A%u5236%u6574%u5408%uFF0C%u7B49%u4ED6%u4EEC%u5B8C%u5168%u76F8%u7B49%u4E86%u518D%u8BF4%0A%3E-%20Such%20a%20leap-of-faith%20refactoring%20is%20exactly%20what%20we%27re%20trying%20to%20avoid%20with%20our%20strategy%20of%20small%20steps%20and%20concrete%20feedback.%20Although%20you%20can%27t%20always%20avoid%20leapy%20refactorings%2C%20you%20can%20reduce%20their%20incidence.%20%u4E0D%u8981%u505A%u8DE8%u8D8A%u5F0F%u7684%u91CD%u6784%u3002%0A%3E-%20Sometimes%20you%20need%20to%20approach%20reconciling%20differences%20backward%u2014that%20is%2C%20think%20about%20how%20the%20last%20step%20of%20the%20change%20could%20be%20trivial%2C%20then%20work%20backward.%20%u53CD%u5411%u601D%u8003%uFF0C%u5C3D%u91CF%u8BA9%u6BCF%u4E00%u6B65%u90FD%u975E%u5E38%u5C0F%28trivial%29%uFF0C%u8FD9%u6837%u65B9%u4FBF%u56DE%u9000%u3002%0A%0A%23%23%23Isolate%20Change%0A%u9694%u79BB%u6539%u52A8%uFF0C%u4E5F%u5C31%u662F%u6211%u4EEC%u901A%u5E38%u505A%u7684%u4E00%u4E9B%u62BD%u8C61%u5C01%u88C5%u3002%u628A%u53D8%u5316%u7684%u90E8%u5206%u7528%u63A5%u53E3%u5305%u88C5%u8D77%u6765%uFF0C%u8FD9%u6837%u5C31%u8FBE%u5230%u4E86%u9694%u79BB%u7684%u6548%u679C%u3002%0A%3ESome%20possible%20ways%20to%20isolate%20change%20are%20Extract%20Method%20%28the%20most%20common%29%2C%20Extract%20Object%2C%20and%20Method%20Object.%0A%0A%23%23%23Migrate%20Data%0A%u4E0D%u77E5%u6240%u4E91%0A%0A%23%23%23Extract%20Method%0A%3EHow%20do%20you%20make%20a%20long%2C%20complicated%20method%20easier%20to%20read%3F%20Turn%20a%20small%20part%20of%20it%20into%20a%20separate%20method%20and%20call%20the%20new%20method.%20%u5C06%u4E00%u4E2A%u5927%u51FD%u6570%u6253%u788E%uFF0C%u4ECE%u4E2D%u63D0%u53D6%u4E00%u4E9B%u65B9%u6CD5%u51FA%u6765%u3002%0A%23%23%23%23%20How%0A%3E1.%20Find%20a%20region%20of%20the%20method%20that%20would%20make%20sense%20as%20its%20own%20method.%20Bodies%20of%0Aloop%2C%20whole%20loops%2C%20and%20branches%20of%20conditionals%20are%20common%20candidates%20for%20extraction.%0A%3E2.%20Make%20sure%20that%20there%20are%20no%20assignments%20to%20temporary%20variables%20declared%20outside%20the%0Ascope%20of%20the%20region%20to%20be%20extracted.%0A%3E3.%20Copy%20the%20code%20from%20the%20old%20method%20to%20the%20new%20method.%20Compile%20it.%0A%3E4.%20For%20each%20temporary%20variable%20or%20parameter%20of%20the%20original%20method%20used%20in%20the%20new%0Amethod%2C%20add%20a%20parameter%20to%20the%20new%20method.%0A%3E5.%20Call%20the%20new%20method%20from%20the%20original%20method.%0A%0A%23%23%23%23Why%0A1.%20%u589E%u5F3A%u53EF%u8BFB%u6027%0A2.%20%u6D88%u9664%u91CD%u590D%uFF0C%u5F53%u4F60%u53D1%u73B0%u6709%u4E24%u4E2A%u5927%u51FD%u6570%uFF0C%u6709%u4E00%u90E8%u5206%u662F%u76F8%u540C%u7684%uFF0C%u90A3%u5C31%u53EF%u4EE5%u63D0%u53D6%u51FA%u6765%u79F0%u4E3A%u65B0%u7684%u65B9%u6CD5%u3002%0A3.%20%u901A%u8FC7inline%u628A%u4E00%u5806%u51FD%u6570%u653E%u5728%u4E00%u8D77%uFF0C%u7136%u540E%u770B%u770B%u54EA%u91CC%u53EF%u4EE5extract%20%uFF08%u8FD9%u4E00%u70B9%u6211%u611F%u89C9%u5E94%u8BE5%u653E%u5728How%u91CC%u9762%uFF0C%u4F5C%u8005%u662F%u60F3%u627F%u4E0A%u542F%u4E0B%uFF0C%u5C06inline%u5427%uFF09%0A%0A%23%23%23Inline%20Method%0Ainline%u5F88%u7384%u4E4E%uFF0C%u5176%u5B9E%u5C31%u662F%u628A%u51FD%u6570%u7684%u4EE3%u7801%u590D%u5236%u5230%u8C03%u7528%u7684%u5730%u65B9%u3002%u597D%u5904%u662F%u4EC0%u4E48%uFF0C%u4F5C%u8005%u6709%u8BDD%u8BF4%uFF1A%0A%3EYou%20might%20like%20the%20second%20version%20better%2C%20or%20you%20might%20not.%20The%20point%20to%20note%20here%20is%20that%20you%20can%20use%20Inline%20Method%20to%20play%20around%20with%20the%20**flow%20of%20control**.%20%0A%u5176%u5B9E%u5C31%u662F%u8BA9%u4F60%u628A%u62BD%u8C61%u51FA%u6765%u7684%u4E1C%u897F%u653E%u5230%u4E00%u8D77%u91CD%u65B0%u62BD%u8C61%uFF0C%u770B%u770B%u4F1A%u4E0D%u4F1A%u6709%u66F4%u597D%u7684%u7ED3%u679C%u3002%u8FD9%u4E5F%u662F%u4E00%u79CD%u91CD%u6784%u7684%u65B9%u6CD5%u3002%0A%0A%23%23%23Extract%20Interface%0A%23%23%23%23How%0A%u5982%u4F55%u5C06%u4E00%u4E2A%u7C7B%u62BD%u8C61%u6210%u4E00%u4E2Ainterface%u3002%u6240%u8C13%u7684interface%u5C31%u662F%u53EA%u6709%u7EAF%u865A%u51FD%u6570%u7684%u7C7B%u3002%0A%3E1.%20Declare%20an%20interface.%20Sometimes%20the%20name%20of%20the%20existing%20class%20should%20be%20the%20name%20of%20the%20interface%2C%20in%20which%20case%20you%20should%20first%20rename%20the%20class.%0A%3E2.%20Have%20the%20existing%20class%20implement%20the%20interface.%0A%3E3.%20Add%20the%20necessary%20methods%20to%20the%20interface%2C%20expanding%20the%20visibility%20of%20the%20methods%20in%20the%20class%20if%20necessary.%0A%3E4.%20Change%20type%20declarations%20from%20the%20class%20to%20the%20interface%20where%20possible.%0A%0A%23%23%23%23Why%0A%u8FD9%u91CC%u63D0%u5230%u4E86%u4E24%u79CD%u60C5%u51B5%uFF1A%0A1.%20%u5F53%u4F60%u9700%u8981%u62BD%u8C61%u51FA%u7236%u7C7B%u65F6%0A2.%20%u5F53%u4F60%u7528%u4E86Mock%20Object%uFF0C%u73B0%u5728%u8981%u4E3A%u8FD9%u4E2Amock%20object%u62BD%u8C61%u51FA%u4E00%u4E2A%u771F%u6B63%u7684%u63A5%u53E3%u65F6%u3002%u4F5C%u8005%u8FD9%u91CC%u63D0%u5230%u4E86%u4E00%u4E2A%u5C0F%u6280%u5DE7%uFF0C%u5728%u8FD9%u79CD%u60C5%u51B5%u4E0B%u547D%u540D%u662F%u4E00%u4E2A%u5934%u75BC%u7684%u95EE%u9898%u3002%u4E0D%u59A8%u5C31%u628A%u65B0%u7684interface%u79F0%u4E3AIXXX%uFF0C%u4F8B%u5982%uFF0C%u539F%u6765%u7684%u53EB%60File%60%uFF0C%u65B0%u7684%u63A5%u53E3%u5C31%u79F0%u4E3A%60IFile%60%0A%0A%23%23%23Move%20Method%0A%u5176%u5B9E%u5C31%u662F%u628A%u4E00%u4E2A%u7C7B%u7684%u65B9%u6CD5%u79FB%u5230%u53E6%u5916%u4E00%u4E2A%u7C7B%u91CC%u9762%u3002%u4F46%u4F5C%u8005%u63D0%u5230%u4E86%u4E00%u79CD%u60C5%u51B5%u4E0B%uFF0C%u8FD9%u4E2A%u65B9%u6CD5%u4E0D%u9002%u7528%u3002%u5C31%u662F%u8FD9%u4E2A%u65B9%u6CD5%u4FEE%u6539%u4E86%u539F%u5148%u90A3%u4E2A%u7C7B%u7684%u6570%u636E%u3002%0A%0A%23%23%23Method%20Object%0A%u8FD9%u4E2A%u8BDD%u9898%u597D%u50CF%u5F88%u65B0%u9C9C%uFF0C%u4E4B%u524D%u770B%u5230%u8FC7%u5F88%u591A%u6B21%uFF0C%u4F46%u5E76%u6CA1%u6709%u6DF1%u7A76%u3002%u5148%u770B%u770BHow%uFF1A%0A%23%23%23%23How%0A%3E1.%20Create%20an%20object%20with%20the%20same%20parameters%20as%20the%20method.%0A%3E2.%20Make%20the%20local%20variables%20also%20instance%20variables%20of%20the%20object.%0A%3E3.%20Create%20one%20method%20called%20run%28%29%2C%20whose%20body%20is%20the%20same%20as%20the%20body%20of%20the%20original%20method.%0A%3E4.%20In%20the%20original%20method%2C%20create%20a%20new%20object%20and%20invoke%20run%28%29.%0A%0A%u6709%u4EC0%u4E48%u597D%u5904%uFF1F%0A%23%23%23%23Why%0A%u4E24%u4E2A%u597D%u5904%0A1.%20%u5728%u6DFB%u52A0%u590D%u6742%u903B%u8F91%u7684%u65F6%u5019%uFF0C%u5982%u679C%u6709%u5BF9%u4E00%u4E2A%u8FC7%u7A0B%u6709%u591A%u79CD%u65B9%u6CD5%uFF0C%u90A3%u521B%u5EFA%u4E00%u4E2A%u65B9%u6CD5%u5BF9%u8C61%uFF0C%u518D%u6269%u5C55%u8D77%u6765%u5C31%u5F88%u7B80%u5355%0A2.%20%u5728%u4E00%u4E2A%u590D%u6742%u51FD%u6570%u91CCextract%20interface%u7684%u65F6%u5019%uFF0C%u901A%u5E38%u4E00%u6BB5code%u9700%u89815%uFF0C6%u4E2A%u53C2%u6570%uFF0C%u8FD9%u6837%u7684%u8BDD%uFF0C%u867D%u7136%u51FD%u6570%u62BD%u53D6%u51FA%u6765%u4E86%uFF0C%u4F46%u662F%u5199%u6CD5%u5E76%u6CA1%u6709%u7B80%u5316%u591A%u5C11%u3002%u8FD9%u65F6%u5019%u5982%u679C%u6709%u65B9%u6CD5%u5BF9%u8C61%uFF0C%u5219%u5728%u7C7B%u91CC%u9762%u662F%u4E00%u4E2A%u72EC%u7ACB%u7684%u540D%u5B57%u7A7A%u95F4%uFF0C%u4F1A%u63D0%u4F9B%u989D%u5916%u7684%u4FBF%u5229%u3002%0A%0A%23%23%23Add%20Parameter%0A%u5C31%u662F%u52A0%u4E00%u4E2A%u53C2%u6570%uFF0C%u6CA1%u4EC0%u4E48%u7279%u522B%u7684%0A%0A%23%23%23Method%20Parameter%20to%20Constructor%20Parameter%0A%u5C31%u662F%u628A%u65B9%u6CD5%u8C03%u7528%u7684%u53C2%u6570%uFF0C%u53D8%u6210%u7C7B%u6210%u5458%u53D8%u91CF%u3002%u4E3A%u4EC0%u4E48%u8981%u8FD9%u4E48%u505A%uFF1F%0A%3EIf%20you%20pass%20the%20same%20parameter%20to%20several%20different%20methods%20in%20the%20same%20object%2C%20then%20you%20can%20simplify%20the%20API%20by%20passing%20the%20parameter%20once%20%28eliminating%20duplication%29.%20You%20can%20run%20this%20refactoring%20in%20reverse%20if%20you%20find%20that%20an%20instance%20variable%20is%20used%20in%20only%20one%20method.%0A%0A%u8FD9%u4E2A%u65B9%u6CD5%u7B80%u5355%u5B9E%u7528%u3002%0A%0A%23%23Chapter%2032.%20Mastering%20TDD%0A%u7ECF%u8FC731%u7AE0%u7684%u5B66%u4E60%uFF0C%u4F5C%u8005%u8BA4%u4E3A%u6211%u4EEC%u5DF2%u7ECF%u5B8C%u5B8C%u5168%u5168%u6709%u80FD%u529B%u6210%u4E3A%u4E00%u4E2A%u5408%u683C%u7684TDD%u7A0B%u5E8F%u5458%u4E86%u3002%u5728%u8FD9%u4E00%u7AE0%u91CC%uFF0C%u4F5C%u8005%u53C8%u603B%u7ED3%u51FA%u5F88%u591ATDD%u521D%u5B66%u8005%u7684%u95EE%u9898%uFF0C%u7ED9%u4E88%u56DE%u7B54%u3002%u7B54%u6848%u90FD%u662F%u4E00%u4E9B%u5F88%u5B9E%u7528%u7684%u5FE0%u544A%uFF0C%u7279%u6458%u53D6%u5982%u4E0B%u3002%0A%0A%23%23%23What%20don%27t%20you%20have%20to%20test%3F%0A%u4F5C%u8005%u5E76%u6CA1%u6709%u76F2%u76EE%u5F3A%u8C03%u6D4B%u8BD5%u91CD%u8981%uFF0C%u4E5F%u6CA1%u6709%u8BF4%u4EFB%u4F55%u5730%u65B9%u90FD%u8981%u5199%u6D4B%u8BD5%u3002%u4F5C%u8005%u8BF4%uFF1A%0A%3EUnless%20you%20have%20reason%20to%20distrust%20it%2C%20don%27t%20test%20code%20from%20others.%20%0A%3E%22Write%20tests%20until%20fear%20is%20transformed%20into%20boredom.%22%0A%0A%u53EA%u5728%u4F60%u4E0D%u76F8%u4FE1%u4F60%u7684code%u65F6%uFF0C%u5BF9%u4ED6%u8FDB%u884C%u6D4B%u8BD5%u3002%u6D4B%u8BD5%u4E5F%u4E0D%u662F%u4E3A%u4E86%u6D4B%u8BD5%u800C%u6D4B%u8BD5%uFF0C%u6216%u8005%u4E3A%u4E86%u4EE3%u7801%u7684%u5065%u58EE%uFF0C%u6D4B%u8BD5%u53EA%u662F%u4E3A%u4E86%u51CF%u5C11%u4F60%u7684%u7126%u8651%u3002%u8BA9%u4F60%u4E0D%u8981%u4ECE%u62C5%u5FC3%u53D8%u6210%u70E6%u8E81%uFF0C%u4EC5%u6B64%u800C%u5DF2%u3002%0A%u54CE%uFF01%u771F%u662F%u918D%u9190%u704C%u9876%uFF01%u76F8%u89C1%u6068%u665A%uFF01%0A%0A%23%23%23How%20do%20you%20know%20if%20you%20have%20good%20tests%3F%0A%u4EC0%u4E48%u6837%u7684%u6D4B%u8BD5%u4E0D%u597D%uFF1F%0A%3E1.%20Long%20setup%20code.%20%u5982%u679C%u4E3A%u4E86%u505A%u4E00%u4E2A%u7B80%u5355%u7684%u6D4B%u8BD5%uFF0C%u53EF%u80FD%u53EA%u6709%u51E0%u884C%u65AD%u8A00%uFF0C%u800C%u4E3A%u6B64%u521B%u5EFA%u4E00%u4E2A%u5DE8%u5927%u7684%u5BF9%u8C61%uFF0C%u90A3%u8BF4%u660E%u54EA%u91CC%u80AF%u5B9A%u51FA%u95EE%u9898%u4E86%u3002%0A%3E2.%20Setup%20duplication.%20%u5982%u679C%u4F60%u53D1%u73B0%u6709%u51E0%u4E2A%u6D4B%u8BD5%u62E5%u6709%u76F8%u540C%u6216%u76F8%u4F3C%u7684setup%u4EE3%u7801%uFF0C%u90A3%u8BF4%u660E%u4F60%u7684%u6D4B%u8BD5%u4E2D%u6709%u91CD%u590D%u7684%u5730%u65B9%u3002%0A%3E3.%20Long%20running%20tests.%20TDD%20tests%20that%20run%20a%20long%20time%20won%27t%20be%20run%20often.%20Suites%20that%20take%20longer%20than%20ten%20minutes%0Ainevitably%20get%20trimmed.%20%u4E00%u4E2A%u6D4B%u8BD5%u5957%u4EF6%u4E0D%u8981%u8D85%u8FC7%u5341%u5206%u949F%u3002%u5426%u5219%uFF0C%u53CD%u590D%u8FD0%u884C%u7684%u673A%u4F1A%u5C31%u4F1A%u6025%u5267%u964D%u4F4E%u3002%0A%3E4.%20Fragile%20tests.%20Tests%20that%20break%20unexpectedly%20suggest%20that%20one%20part%20of%20the%20application%20is%20surprisingly%20affecting%20another%20part.%20%u5982%u679C%u6D4B%u8BD5%u88AB%u65E0%u60C5%u6253%u65AD%uFF0C%u8BF4%u660E%u4F60%u7684%u7A0B%u5E8F%u91CC%u6709%u8026%u5408%u3002%0A%0A%23%23%23How%20does%20TDD%20lead%20to%20frameworks%3F%0A%u53C8%u88AB%u918D%u9190%u704C%u9876%u4E86%uFF01%0A%3ETDD%20appears%20to%20stand%20this%20advice%20on%20its%20head%3A%20%3Cb%3E%3Cfont%20color%3D%22IndianRed%22%3E%22Code%20for%20tomorrow%2C%20design%20for%20today.%22%20%3C/font%3E%3C/b%3EHere%27s%20what%20happens%20in%20practice.%20%u4E0D%u8981%u4E3A%u672A%u6765%u8003%u8651%u90A3%u4E48%u591A%uFF0C%u7B80%u5355%u7684%u8BA9%u6D4B%u8BD5%u901A%u8FC7%uFF0C%u7136%u540E%u53CA%u65F6%u7684%u505A%u91CD%u6784%u6D88%u9664%u91CD%u590D%uFF0C%u53CD%u800C%u80FD%u8FBE%u5230%u66F4%u597D%u7684%u8BBE%u8BA1%u6846%u67B6%uFF08frameworks%uFF09%u3002%0A%0A%23%23%23How%20much%20feedback%20do%20you%20need%3F%0A%u8FD9%u91CC%u8BF4%u7684%u548C%u4E4B%u524D%u4E00%u76F4%u5F3A%u8C03%u7684%u601D%u60F3%u5F88%u4E00%u81F4%0A%3E%20If%20our%20knowledge%20of%20the%20implementation%20gives%20us%20confidence%20even%20without%20a%20test%2C%20then%20we%20will%20not%20write%20that%20test.%20%0A%0A%23%23%23When%20should%20you%20delete%20tests%3F%0A%3E1.%20The%20first%20criterion%20for%20your%20tests%20is%20**confidence**.%20Never%20delete%20a%20test%20if%20it%20reduces%20your%20confidence%20in%20the%20behavior%20of%20the%20system.%0A%3E2.%20The%20second%20criterion%20is%20**communication**.%20If%20you%20have%20two%20tests%20that%20exercise%20the%20same%20path%20through%20the%20code%2C%20but%20they%20speak%20to%20different%20scenarios%20for%20a%20reader%2C%20leave%20them%20alone.%0A%0A%23%23%23Can%20you%20drive%20development%20with%20application-level%20tests%3F%0A%u4E0D%u8981%u5C1D%u8BD5%u505A%u5E94%u7528%u7EA7%u522B%u7684test%0A%0A%23%23%23How%20do%20you%20switch%20to%20TDD%20midstream%3F%0A%u8FD9%u4E5F%u662F%u6211%u60F3%u95EE%u7684%uFF0C%u4F5C%u8005%u7684%u56DE%u7B54%u662F%u8FD9%u672C%u4E66%u5C31%u662F%u4E3A%u4E86%u8FD9%u4E2A%u800C%u5199%u7684%u3002%0A%3EThere%20is%20a%20whole%20book%20%28or%20books%29%20to%20be%20written%20about%20switching%20to%20TDD%20when%20you%20have%20lots%20of%20code.%20What%20follows%20is%20necessarily%20only%20a%20teaser.%0A%0A%u600E%u4E48%u505A%uFF1F%0A1.%20So%20first%20we%20have%20to%20decide%20to%20limit%20the%20scope%20of%20our%20changes.%20%0A2.%20Second%2C%20we%20have%20to%20break%20the%20deadlock%20between%20tests%20and%20refactoring.%20%0A%093.%20%u600E%u4E48%u83B7%u5F97feedback%0A%09%091%29%20We%20can%20get%20feedback%20other%20ways%20than%20with%20tests%2C%20like%20working%20very%20carefully%20and%20with%20a%20partner.%20%0A%09%092%29%20We%20can%20get%20feedback%20at%20a%20gross%20level%2C%20like%20system-level%20tests%20that%20we%20know%20aren%27t%20adequate%20but%20give%20us%20some%20confidence.%20With%20this%20feedback%2C%20we%20can%20make%20the%20areas%20we%20have%20to%20change%20more%20accepting%20of%20change.%0A%0A%23%23%23How%20does%20TDD%20relate%20to%20the%20practices%20of%20Extreme%20Programming%3F%0A%u540D%u8BCD%u89E3%u91CA%uFF1A%5BExtreme%20Programming%20-%20XP%5D%28http%3A//baike.baidu.com/link%3Furl%3DNgfFcw4hxjOQHRtEArm4buq177_Xw562MDhrOykDcd-0EOv1xB_Oz3eslAn9X65cFdUel2xI7tTiqB3Gqyz3Ja%29%u6781%u9650%u7F16%u7A0B%0A%3E%u6781%u9650%u7F16%u7A0B%u662F%u4E00%u4E2A%u8F7B%u91CF%u7EA7%u7684%u3001%u7075%u5DE7%u7684%u8F6F%u4EF6%u5F00%u53D1%u65B9%u6CD5%uFF1B%u540C%u65F6%u5B83%u4E5F%u662F%u4E00%u4E2A%u975E%u5E38%u4E25%u8C28%u548C%u5468%u5BC6%u7684%u65B9%u6CD5%u3002%u5B83%u7684%u57FA%u7840%u548C%u4EF7%u503C%u89C2%u662F%u4EA4%u6D41%u3001%u6734%u7D20%u3001%u53CD%u9988%u548C%u52C7%u6C14%uFF1B%u5373%uFF0C%u4EFB%u4F55%u4E00%u4E2A%u8F6F%u4EF6%u9879%u76EE%u90FD%u53EF%u4EE5%u4ECE%u56DB%u4E2A%u65B9%u9762%u5165%u624B%u8FDB%u884C%u6539%u5584%uFF1A%u52A0%u5F3A%u4EA4%u6D41%uFF1B%u4ECE%u7B80%u5355%u505A%u8D77%uFF1B%u5BFB%u6C42%u53CD%u9988%uFF1B%u52C7%u4E8E%u5B9E%u4E8B%u6C42%u662F%u3002XP%u662F%u4E00%u79CD%u8FD1%u87BA%u65CB%u5F0F%u7684%u5F00%u53D1%u65B9%u6CD5%uFF0C%u5B83%u5C06%u590D%u6742%u7684%u5F00%u53D1%u8FC7%u7A0B%u5206%u89E3%u4E3A%u4E00%u4E2A%u4E2A%u76F8%u5BF9%u6BD4%u8F83%u7B80%u5355%u7684%u5C0F%u5468%u671F%uFF1B%u901A%u8FC7%u79EF%u6781%u7684%u4EA4%u6D41%u3001%u53CD%u9988%u4EE5%u53CA%u5176%u5B83%u4E00%u7CFB%u5217%u7684%u65B9%u6CD5%uFF0C%u5F00%u53D1%u4EBA%u5458%u548C%u5BA2%u6237%u53EF%u4EE5%u975E%u5E38%u6E05%u695A%u5F00%u53D1%u8FDB%u5EA6%u3001%u53D8%u5316%u3001%u5F85%u89E3%u51B3%u7684%u95EE%u9898%u548C%u6F5C%u5728%u7684%u56F0%u96BE%u7B49%uFF0C%u5E76%u6839%u636E%u5B9E%u9645%u60C5%u51B5%u53CA%u65F6%u5730%u8C03%u6574%u5F00%u53D1%u8FC7%u7A0B%u3002%0A%0ATDD%u4E0EXP%u7684%u5171%u901A%u4E4B%u5904%uFF1A%0A-%20Pairing%0A-%20Work%20fresh%0A-%20Continuous%20integration%0A-%20Simple%20design%0A-%20Refactoring%0A-%20Continuous%20delivery

Edit

学习笔记 mongo

名词解释

shard

官方文档Sharding Introduction

Sharding is a method for storing data across multiple machines.

中文称为分片。参考8天学通MongoDB——第六天 分片技术

sharded cluster

片簇

replica set

A replica set in MongoDB is a group of mongod processes that maintain the same data set. Replica sets provide redundancy and high availability, and are the basis for all production deployments.

每一个replica set可以有自己的tag来控制read preference和write concern,参考官方文档Configure Replica Set Tag Sets

write concern

官方文档Write Concern

Write concern describes the level of acknowledgement requested from MongoDB for write operations to a standalone mongod or to replica sets or to sharded clusters. In sharded clusters, mongos instances will pass the write concern on to the shards.

Write concern can include the following fields:

{ w: \<value\>, j: \<boolean\>, wtimeout: \<number\> }

  • w: 1 - 有acknowledgement,默认值; 0 - 没有acknowledgement,数据库可能把错误信息传给客户端
  • w: ‘majority’ & ‘\\’
    • 前者:Requests acknowledgment that write operations have propagated to the majority of voting nodes [1], including the primary, and have been written to the on-disk journal for these nodes. 此选项包含了j: true。
    • 后者:Requests acknowledgement that the write operations have propagated to a replica set member with the specified tag.
  • j: true & false, 决定是否写journal
  • wtimeout: This option specifies a time limit, in milliseconds, for the write concern. wtimeout is only applicable for w values greater than 1.

If you specify w: 0 but include j: true, the j: true prevails to request acknowledgement from the standalone mongod or the primary of a replica set.

journal

A sequential, binary transaction log used to bring the database into a valid state in the event of a hard shutdown. Journaling writes data first to the journal and then to the core data files. MongoDB enables journaling by default for 64-bit builds of MongoDB version 2.0 and newer. Journal files are pre-allocated and exist as files in the data directory. See Journaling.

mongos

mongos for “MongoDB Shard,” is a routing service for MongoDB shard configurations that processes queries from the application layer, and determines the location of this data in the sharded cluster, in order to complete these operations. From the perspective of the application, a mongos instance behaves identically to any other MongoDB instance.

mongos vs. mongod

mongodb采用将集合进行拆分,然后将拆分的数据均摊到几个片上的一种解决方案。

连接mongo数据库

我收集了好几篇讲如何更好的连接mongo数据库的文章。参考下面的笔记:
mongodb驱动的正确使用方法
mongodb 连接的问题
node-mongo-native1.3.19连接mongo的最优方法
还有node.js的MongoDB driverd的官方介绍:
MongoClient or how to connect in a new and better way
还有人用连接池的
生产级Nodejs开发实践-使用连接池

Old Style

先看一段代码

1.var server_options={};
2.var db_options={w:-1};
3.
4.var mongodb = require("mongodb"),
5. mongoserver = new mongodb.Server('localhost', 27017,server_options ),
6. db = new mongodb.Db('test', mongoserver, db_options);
7.
8.var http=require('http');
9.
10.var server=http.createServer(function(req,res){
11. db.open(function(err,db){
12. if(err)return console.error(err);
13. console.log('* mongodb connected');
14. db.collection('foo').save({test:1},function(err,result){
15. res.end(JSON.stringify(result,null,2));
16. db.close();
17. });
18. })
19.
20.});

每来一个http请求,就开关一次数据库。更搞的是,new mongodb.Server的参数server_options里可以有一个poolSize,如果不指定的话,默认值就是5,也就是说每开一次数据库,实际上是开了5个数据库连接。这也就是为什么我们经常在数据库运行终端中经常至少看见5个connections的原因。这也是为什么我们connections疯涨的原因。
另外在old style操作代码中,还有一点值得注意的是,close是不会close读连接的,它只会close写连接,要强制close所有连接要用close(true)。

close
Close the current db connection, including all the child db instances. Emits close event and calls optional callback.

close([forceClose], callback)
Arguments:

  • [forceClose] (boolean) – connection can never be reused.
  • callback (function) – this will be called after executing this method. The first parameter will contain the Error object if an error occurred, or null otherwise. While the second parameter will contain the results or null if an error occurred.

Returns: null

New Style

参考官方代码:

1.var express = require('express'); 
2.var mongodb = require('mongodb'); var app = express();
3.
4.var MongoClient = require('mongodb').MongoClient;
5.var db;
6.
7.// Initialize connection once MongoClient.connect("mongodb://localhost:27017/integration_test", {
8. db: {
9. native_parser: false
10. },
11. server: {
12. socketOptions: {
13. connectTimeoutMS: 500
14. }
15. },
16. replSet: {},
17. mongos: {}
18.},function(err, database) {
19. if(err) throw err;
20. db = database;
21.
22. // Start the application after the database connection is ready
23. app.listen(3000);
24. console.log("Listening on port 3000");
25.});
26.
27.// Reuse database object in request handlers
28.app.get("/", function(req, res) {
29. db.collection("replicaset_mongo_client_collection").find({},function(err, docs) {
30. docs.each(function(err, doc) {
31. if(doc) {
32. console.log(doc);
33. }
34. else {
35. res.end();
36. }
37. });
38. });
39.});

MongoClient.connect options

主要有来自4个方面的设置:

  • db
  • server
  • replica set
  • mongos
    后两个针对sharding,这次我们主要关注前两个。从官方文档里挑出几条配置罗列一下:

db: A hash of options at the db level overriding or adjusting functionality not supported by the url

  • w, {Number/String, > -1 || ‘majority’} the write concern for the operation where < 1 is no acknowledgment of write and w >= 1 or w = ‘majority’ acknowledges the write
  • wtimeout, {Number, 0} set the timeout for waiting for write concern to finish (combines with w option)
  • fsync, (Boolean, default:false) write waits for fsync before returning
  • journal, (Boolean, default:false) write waits for journal sync before returning
  • readPreference {String}, the preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST).
  • native_parser {Boolean, default:false}, use c++ bson parser.
  • forceServerObjectId {Boolean, default:false}, force server to create _id fields instead of client.
  • pkFactory {Object}, object overriding the basic ObjectID primary key generation.
  • serializeFunctions {Boolean, default:false}, serialize functions.
  • raw {Boolean, default:false}, perform operations using raw bson buffers.
  • recordQueryStats {Boolean, default:false}, record query statistics during execution.
  • retryMiliSeconds {Number, default:5000}, number of milliseconds between retries.
  • numberOfRetries {Number, default:5}, number of retries off connection.
  • bufferMaxEntries {Number, default: -1}, sets a cap on how many operations the driver will buffer up before giving up on getting a working connection, default is -1 which is unlimited.

server: A hash of options at the server level not supported by the url.

  • readPreference {String, default:null}, set’s the read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST)
  • ssl {Boolean, default:false}, use ssl connection (needs to have a mongod server with ssl support)
  • slaveOk {Boolean, default:false}, legacy option allowing reads from secondary, use readPrefrence instead.
  • poolSize {Number, default:1}, number of connections in the connection pool, set to 1 as default for legacy reasons.
  • socketOptions {Object, default:null}, an object containing socket options to use (noDelay:(boolean), keepAlive:(number), connectTimeoutMS:(number), socketTimeoutMS:(number))
  • logger {Object, default:null}, an object representing a logger that you want to use, needs to support functions debug, log, error ({error:function(message, object) {}, log:function(message, object) {}, debug:function(message, object) {}}).
  • auto_reconnect {Boolean, default:false}, reconnect on error.
  • disableDriverBSONSizeCheck {Boolean, default:false}, force the server to error if the BSON message is to big

我们关注的选项,我用粗体做了标注。

另外我们在前文中提到,有些文章推荐用generic-pool为mongo连接提供连接池。但是,我发现MongoClient.connect的option里就有poolSize。在实际项目中直接将之赋为100,也可以得到不错的效果。

%23MongoDB%u8FDE%u63A5%0A@%28%u5B66%u4E60%u7B14%u8BB0%29%5Bmongo%5D%0A%23%23%u540D%u8BCD%u89E3%u91CA%0A%23%23%23%20shard%0A%u5B98%u65B9%u6587%u6863%5BSharding%20Introduction%5D%28https%3A//docs.mongodb.org/manual/core/sharding-introduction/%29%uFF1A%0A%3E%20Sharding%20is%20a%20method%20for%20storing%20data%20across%20multiple%20machines.%0A%0A%u4E2D%u6587%u79F0%u4E3A%u5206%u7247%u3002%u53C2%u8003%5B8%u5929%u5B66%u901AMongoDB%u2014%u2014%u7B2C%u516D%u5929%20%u5206%u7247%u6280%u672F%5D%28https%3A//www.evernote.com/shard/s24/nl/697376823/c9ca296a-ae2b-4833-bcb5-a37213b4cafc/%29%u3002%0A%0A%23%23%23%20sharded%20cluster%0A%u7247%u7C07%0A%0A%23%23%23%20replica%20set%0A%3EA%20replica%20set%20in%20MongoDB%20is%20a%20group%20of%20mongod%20processes%20that%20maintain%20the%20same%20data%20set.%20Replica%20sets%20provide%20redundancy%20and%20high%20availability%2C%20and%20are%20the%20basis%20for%20all%20production%20deployments.%20%0A%0A%u6BCF%u4E00%u4E2Areplica%20set%u53EF%u4EE5%u6709%u81EA%u5DF1%u7684tag%u6765%u63A7%u5236read%20preference%u548Cwrite%20concern%uFF0C%u53C2%u8003%u5B98%u65B9%u6587%u6863%5BConfigure%20Replica%20Set%20Tag%20Sets%5D%28https%3A//docs.mongodb.org/manual/tutorial/configure-replica-set-tag-sets/%23replica-set-configuration-tag-sets%29%u3002%0A%0A%23%23%23%20write%20concern%0A%u5B98%u65B9%u6587%u6863%5BWrite%20Concern%5D%28https%3A//docs.mongodb.org/manual/reference/write-concern/%29%0A%3EWrite%20concern%20describes%20the%20level%20of%20acknowledgement%20requested%20from%20MongoDB%20for%20write%20operations%20to%20a%20standalone%20mongod%20or%20to%20replica%20sets%20or%20to%20sharded%20clusters.%20In%20sharded%20clusters%2C%20mongos%20instances%20will%20pass%20the%20write%20concern%20on%20to%20the%20shards.%0A%0AWrite%20concern%20can%20include%20the%20following%20fields%3A%0A%3E%7B%20w%3A%20%5C%3Cvalue%5C%3E%2C%20j%3A%20%5C%3Cboolean%5C%3E%2C%20wtimeout%3A%20%5C%3Cnumber%5C%3E%20%7D%0A%3E%20-%20w%3A%201%20-%20%u6709acknowledgement%uFF0C%u9ED8%u8BA4%u503C%3B%200%20-%20%u6CA1%u6709acknowledgement%uFF0C%u6570%u636E%u5E93%u53EF%u80FD%u628A%u9519%u8BEF%u4FE1%u606F%u4F20%u7ED9%u5BA2%u6237%u7AEF%0A%3E%20-%20w%3A%20%u2018majority%u2019%20%26%20%27%5C%3Ctag%20set%3E%5C%27%0A%09*%20%u524D%u8005%uFF1ARequests%20acknowledgment%20that%20write%20operations%20have%20propagated%20to%20the%20majority%20of%20voting%20nodes%20%5B1%5D%2C%20including%20the%20primary%2C%20**and%20have%20been%20written%20to%20the%20on-disk%20journal%20for%20these%20nodes**.%20%u6B64%u9009%u9879%u5305%u542B%u4E86j%3A%20true%u3002%0A%09*%20%u540E%u8005%uFF1ARequests%20acknowledgement%20that%20the%20write%20operations%20have%20propagated%20to%20a%20replica%20set%20member%20with%20the%20specified%20tag.%0A%3E%20-%20j%3A%20true%20%26%20false%2C%20%u51B3%u5B9A%u662F%u5426%u5199journal%0A%3E%20-%20wtimeout%3A%20This%20option%20specifies%20a%20time%20limit%2C%20in%20milliseconds%2C%20for%20the%20write%20concern.%20wtimeout%20is%20only%20applicable%20for%20w%20values%20greater%20than%201.%0A%0A%3EIf%20you%20specify%20w%3A%200%20but%20include%20j%3A%20true%2C%20the%20j%3A%20true%20prevails%20to%20request%20acknowledgement%20from%20the%20standalone%20mongod%20or%20the%20primary%20of%20a%20replica%20set.%0A%0A%23%23%23%20journal%0A%3E%20A%20sequential%2C%20binary%20transaction%20log%20used%20to%20bring%20the%20database%20into%20a%20valid%20state%20in%20the%20event%20of%20a%20hard%20shutdown.%20Journaling%20writes%20data%20first%20to%20the%20journal%20and%20then%20to%20the%20core%20data%20files.%20MongoDB%20enables%20journaling%20by%20default%20for%2064-bit%20builds%20of%20MongoDB%20version%202.0%20and%20newer.%20Journal%20files%20are%20pre-allocated%20and%20exist%20as%20files%20in%20the%20data%20directory.%20See%20%5BJournaling%5D%28https%3A//docs.mongodb.org/manual/core/journaling/%29.%0A%0A%23%23%23%20mongos%0A**%5Bmongos%5D%28https%3A//docs.mongodb.org/manual/reference/program/mongos/%29**%20for%20%u201CMongoDB%20Shard%2C%u201D%20is%20a%20routing%20service%20for%20MongoDB%20shard%20configurations%20that%20processes%20queries%20from%20the%20application%20layer%2C%20and%20determines%20the%20location%20of%20this%20data%20in%20the%20sharded%20cluster%2C%20in%20order%20to%20complete%20these%20operations.%20From%20the%20perspective%20of%20the%20application%2C%20a%20mongos%20instance%20behaves%20identically%20to%20any%20other%20MongoDB%20instance.%0A%0A%23%23mongos%20vs.%20mongod%0Amongodb%u91C7%u7528%u5C06%u96C6%u5408%u8FDB%u884C%u62C6%u5206%uFF0C%u7136%u540E%u5C06%u62C6%u5206%u7684%u6570%u636E%u5747%u644A%u5230%u51E0%u4E2A%u7247%u4E0A%u7684%u4E00%u79CD%u89E3%u51B3%u65B9%u6848%u3002%0A%21%5BAlt%20text%5D%28./1454111696209.png%29%0A%u7B11%u8138%u662F%u7528%u6237%uFF0Cconfig%u662Fconfig%u670D%u52A1%u5668%uFF0C%u4E5F%u662F%u4E00%u4E2Amongod%u7684%u6570%u636E%u5E93%0A%0A%23%23%u8FDE%u63A5mongo%u6570%u636E%u5E93%0A%u6211%u6536%u96C6%u4E86%u597D%u51E0%u7BC7%u8BB2%u5982%u4F55%u66F4%u597D%u7684%u8FDE%u63A5mongo%u6570%u636E%u5E93%u7684%u6587%u7AE0%u3002%u53C2%u8003%u4E0B%u9762%u7684%u7B14%u8BB0%uFF1A%0A%5Bmongodb%u9A71%u52A8%u7684%u6B63%u786E%u4F7F%u7528%u65B9%u6CD5%5D%28https%3A//www.evernote.com/shard/s24/nl/697376807/5bc8092d-bccf-40cd-9ad5-9ec0b28611dd/%29%0A%5Bmongodb%20%u8FDE%u63A5%u7684%u95EE%u9898%5D%28https%3A//www.evernote.com/shard/s24/nl/697376807/bb648a45-3206-4fb9-b0a4-e4024638f6f5/%29%0A%5Bnode-mongo-native1.3.19%u8FDE%u63A5mongo%u7684%u6700%u4F18%u65B9%u6CD5%5D%28https%3A//www.evernote.com/shard/s24/nl/697376807/eac602ff-a873-4d71-bc7b-0a5650905d76/%29%0A%u8FD8%u6709node.js%u7684MongoDB%20driverd%u7684%u5B98%u65B9%u4ECB%u7ECD%uFF1A%0A%5BMongoClient%20or%20how%20to%20connect%20in%20a%20new%20and%20better%20way%5D%28https%3A//www.evernote.com/shard/s24/nl/697376807/ee17ecea-566c-4f2f-8138-79cb8ea25d8f/%29%0A%u8FD8%u6709%u4EBA%u7528%u8FDE%u63A5%u6C60%u7684%0A%5B%u751F%u4EA7%u7EA7Nodejs%u5F00%u53D1%u5B9E%u8DF5-%u4F7F%u7528%u8FDE%u63A5%u6C60%5D%28https%3A//www.evernote.com/shard/s24/nl/697376807/53bb4dd3-fd7f-4780-93ae-67faee3637a5/%29%0A%23%23%23Old%20Style%0A%u5148%u770B%u4E00%u6BB5%u4EE3%u7801%0A%60%60%60javascript%0Avar%20server_options%3D%7B%7D%3B%0Avar%20db_options%3D%7Bw%3A-1%7D%3B%0A%0Avar%20mongodb%20%3D%20require%28%22mongodb%22%29%2C%0A%20%20%20%20mongoserver%20%3D%20new%20mongodb.Server%28%27localhost%27%2C%2027017%2Cserver_options%20%29%2C%0A%20%20%20%20db%20%3D%20new%20mongodb.Db%28%27test%27%2C%20mongoserver%2C%20db_options%29%3B%0A%0Avar%20http%3Drequire%28%27http%27%29%3B%0A%0Avar%20server%3Dhttp.createServer%28function%28req%2Cres%29%7B%0A%20%20%20%20db.open%28function%28err%2Cdb%29%7B%0A%20%20%20%20%20%20%20%20if%28err%29return%20console.error%28err%29%3B%0A%20%20%20%20%20%20%20%20console.log%28%27*%20mongodb%20connected%27%29%3B%0A%20%20%20%20%20%20%20%20db.collection%28%27foo%27%29.save%28%7Btest%3A1%7D%2Cfunction%28err%2Cresult%29%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20res.end%28JSON.stringify%28result%2Cnull%2C2%29%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20db.close%28%29%3B%0A%20%20%20%20%20%20%20%20%7D%29%3B%0A%20%20%20%20%7D%29%0A%0A%7D%29%3B%0A%60%60%60%0A%u6BCF%u6765%u4E00%u4E2Ahttp%u8BF7%u6C42%uFF0C%u5C31%u5F00%u5173%u4E00%u6B21%u6570%u636E%u5E93%u3002%u66F4%u641E%u7684%u662F%uFF0C%60new%20mongodb.Server%60%u7684%u53C2%u6570server_options%u91CC%u53EF%u4EE5%u6709%u4E00%u4E2ApoolSize%uFF0C%u5982%u679C%u4E0D%u6307%u5B9A%u7684%u8BDD%uFF0C%u9ED8%u8BA4%u503C%u5C31%u662F5%uFF0C%u4E5F%u5C31%u662F%u8BF4%u6BCF%u5F00%u4E00%u6B21%u6570%u636E%u5E93%uFF0C%u5B9E%u9645%u4E0A%u662F%u5F00%u4E865%u4E2A%u6570%u636E%u5E93%u8FDE%u63A5%u3002%u8FD9%u4E5F%u5C31%u662F%u4E3A%u4EC0%u4E48%u6211%u4EEC%u7ECF%u5E38%u5728%u6570%u636E%u5E93%u8FD0%u884C%u7EC8%u7AEF%u4E2D%u7ECF%u5E38%u81F3%u5C11%u770B%u89C15%u4E2Aconnections%u7684%u539F%u56E0%u3002%u8FD9%u4E5F%u662F%u4E3A%u4EC0%u4E48%u6211%u4EECconnections%u75AF%u6DA8%u7684%u539F%u56E0%u3002%0A%u53E6%u5916%u5728old%20style%u64CD%u4F5C%u4EE3%u7801%u4E2D%uFF0C%u8FD8%u6709%u4E00%u70B9%u503C%u5F97%u6CE8%u610F%u7684%u662F%uFF0Cclose%u662F%u4E0D%u4F1Aclose%u8BFB%u8FDE%u63A5%u7684%uFF0C%u5B83%u53EA%u4F1Aclose%u5199%u8FDE%u63A5%uFF0C%u8981%u5F3A%u5236close%u6240%u6709%u8FDE%u63A5%u8981%u7528close%28true%29%u3002%0A%3E**close**%0AClose%20the%20current%20db%20connection%2C%20including%20all%20the%20child%20db%20instances.%20Emits%20close%20event%20and%20calls%20optional%20callback.%0A%0Aclose%28%5B*forceClose*%5D%2C%20*callback*%29%0A**Arguments**%3A%09%0A*%20**%5BforceClose%5D**%20%28*boolean*%29%20%u2013%20connection%20can%20never%20be%20reused.%0A*%20**callback**%20%28*function*%29%20%u2013%20this%20will%20be%20called%20after%20executing%20this%20method.%20The%20first%20parameter%20will%20contain%20the%20Error%20object%20if%20an%20error%20occurred%2C%20or%20null%20otherwise.%20While%20the%20second%20parameter%20will%20contain%20the%20results%20or%20null%20if%20an%20error%20occurred.%0A%0A**Returns**%3A%09null%0A%23%23%23New%20Style%0A%u53C2%u8003%u5B98%u65B9%u4EE3%u7801%uFF1A%0A%60%60%60javascript%0Avar%20express%20%3D%20require%28%27express%27%29%3B%20%0Avar%20mongodb%20%3D%20require%28%27mongodb%27%29%3B%20var%20app%20%3D%20express%28%29%3B%20%0A%0Avar%20MongoClient%20%3D%20require%28%27mongodb%27%29.MongoClient%3B%20%0Avar%20db%3B%20%0A%0A//%20Initialize%20connection%20once%20MongoClient.connect%28%22mongodb%3A//localhost%3A27017/integration_test%22%2C%20%7B%20%0A%09db%3A%20%7B%20%0A%09%09native_parser%3A%20false%20%0A%09%7D%2C%20%0A%09server%3A%20%7B%20%0A%09%09socketOptions%3A%20%7B%20%0A%09%09%09connectTimeoutMS%3A%20500%20%0A%09%09%7D%20%0A%09%7D%2C%20%0A%09replSet%3A%20%7B%7D%2C%20%0A%09mongos%3A%20%7B%7D%20%0A%7D%2Cfunction%28err%2C%20database%29%20%7B%20%0A%09if%28err%29%20throw%20err%3B%20%0A%09db%20%3D%20database%3B%20%0A%0A%09//%20Start%20the%20application%20after%20the%20database%20connection%20is%20ready%20%0A%09app.listen%283000%29%3B%20%0A%09console.log%28%22Listening%20on%20port%203000%22%29%3B%20%0A%7D%29%3B%20%0A%0A//%20Reuse%20database%20object%20in%20request%20handlers%20%0Aapp.get%28%22/%22%2C%20function%28req%2C%20res%29%20%7B%20%09%09%09%0A%09db.collection%28%22replicaset_mongo_client_collection%22%29.find%28%7B%7D%2Cfunction%28err%2C%20docs%29%20%7B%20%0A%09%09docs.each%28function%28err%2C%20doc%29%20%7B%20%0A%09%09%09if%28doc%29%20%7B%20%0A%09%09%09%09console.log%28doc%29%3B%20%0A%09%09%09%7D%20%0A%09%09%09else%20%7B%0A%09%09%09%09res.end%28%29%3B%20%0A%09%09%09%7D%20%0A%09%09%7D%29%3B%0A%09%7D%29%3B%20%0A%7D%29%3B%0A%60%60%60%0A%23%23%23MongoClient.connect%20options%0A%u4E3B%u8981%u6709%u6765%u81EA4%u4E2A%u65B9%u9762%u7684%u8BBE%u7F6E%uFF1A%0A-%20db%0A-%20server%0A-%20replica%20set%0A-%20mongos%0A%u540E%u4E24%u4E2A%u9488%u5BF9sharding%uFF0C%u8FD9%u6B21%u6211%u4EEC%u4E3B%u8981%u5173%u6CE8%u524D%u4E24%u4E2A%u3002%u4ECE%u5B98%u65B9%u6587%u6863%u91CC%u6311%u51FA%u51E0%u6761%u914D%u7F6E%u7F57%u5217%u4E00%u4E0B%uFF1A%0A%23%23%23%23db%3A%20A%20hash%20of%20options%20at%20the%20db%20level%20overriding%20or%20adjusting%20functionality%20not%20supported%20by%20the%20url%0A-%20**w**%2C%20%7BNumber/String%2C%20%3E%20-1%20%7C%7C%20%u2018majority%u2019%7D%20the%20write%20concern%20for%20the%20operation%20where%20%3C%201%20is%20no%20acknowledgment%20of%20write%20and%20w%20%3E%3D%201%20or%20w%20%3D%20%u2018majority%u2019%20acknowledges%20the%20write%0A-%20wtimeout%2C%20%7BNumber%2C%200%7D%20set%20the%20timeout%20for%20waiting%20for%20write%20concern%20to%20finish%20%28combines%20with%20w%20option%29%0A-%20fsync%2C%20%28Boolean%2C%20default%3Afalse%29%20write%20waits%20for%20fsync%20before%20returning%0A-%20**journal**%2C%20%28Boolean%2C%20default%3Afalse%29%20write%20waits%20for%20journal%20sync%20before%20returning%0A-%20readPreference%20%7BString%7D%2C%20the%20preferred%20read%20preference%20%28ReadPreference.PRIMARY%2C%20ReadPreference.PRIMARY_PREFERRED%2C%20ReadPreference.SECONDARY%2C%20ReadPreference.SECONDARY_PREFERRED%2C%20ReadPreference.NEAREST%29.%0A-%20native_parser%20%7BBoolean%2C%20default%3Afalse%7D%2C%20use%20c++%20bson%20parser.%0A-%20forceServerObjectId%20%7BBoolean%2C%20default%3Afalse%7D%2C%20force%20server%20to%20create%20_id%20fields%20instead%20of%20client.%0A-%20pkFactory%20%7BObject%7D%2C%20object%20overriding%20the%20basic%20ObjectID%20primary%20key%20generation.%0A-%20serializeFunctions%20%7BBoolean%2C%20default%3Afalse%7D%2C%20serialize%20functions.%0A-%20raw%20%7BBoolean%2C%20default%3Afalse%7D%2C%20perform%20operations%20using%20raw%20bson%20buffers.%0A-%20recordQueryStats%20%7BBoolean%2C%20default%3Afalse%7D%2C%20record%20query%20statistics%20during%20execution.%0A-%20retryMiliSeconds%20%7BNumber%2C%20default%3A5000%7D%2C%20number%20of%20milliseconds%20between%20retries.%0A-%20numberOfRetries%20%7BNumber%2C%20default%3A5%7D%2C%20number%20of%20retries%20off%20connection.%0A-%20bufferMaxEntries%20%7BNumber%2C%20default%3A%20-1%7D%2C%20sets%20a%20cap%20on%20how%20many%20operations%20the%20driver%20will%20buffer%20up%20before%20giving%20up%20on%20getting%20a%20working%20connection%2C%20default%20is%20-1%20which%20is%20unlimited.%0A%23%23%23%23server%3A%20A%20hash%20of%20options%20at%20the%20server%20level%20not%20supported%20by%20the%20url.%0A-%20readPreference%20%7BString%2C%20default%3Anull%7D%2C%20set%u2019s%20the%20read%20preference%20%28ReadPreference.PRIMARY%2C%20ReadPreference.PRIMARY_PREFERRED%2C%20ReadPreference.SECONDARY%2C%20ReadPreference.SECONDARY_PREFERRED%2C%20ReadPreference.NEAREST%29%0A-%20ssl%20%7BBoolean%2C%20default%3Afalse%7D%2C%20use%20ssl%20connection%20%28needs%20to%20have%20a%20mongod%20server%20with%20ssl%20support%29%0A-%20slaveOk%20%7BBoolean%2C%20default%3Afalse%7D%2C%20legacy%20option%20allowing%20reads%20from%20secondary%2C%20use%20readPrefrence%20instead.%0A-%20**poolSize**%20%7BNumber%2C%20default%3A1%7D%2C%20number%20of%20connections%20in%20the%20connection%20pool%2C%20set%20to%201%20as%20default%20for%20legacy%20reasons.%0A-%20socketOptions%20%7BObject%2C%20default%3Anull%7D%2C%20an%20object%20containing%20socket%20options%20to%20use%20%28noDelay%3A%28boolean%29%2C%20keepAlive%3A%28number%29%2C%20connectTimeoutMS%3A%28number%29%2C%20socketTimeoutMS%3A%28number%29%29%0A-%20logger%20%7BObject%2C%20default%3Anull%7D%2C%20an%20object%20representing%20a%20logger%20that%20you%20want%20to%20use%2C%20needs%20to%20support%20functions%20debug%2C%20log%2C%20error%20%28%7Berror%3Afunction%28message%2C%20object%29%20%7B%7D%2C%20log%3Afunction%28message%2C%20object%29%20%7B%7D%2C%20debug%3Afunction%28message%2C%20object%29%20%7B%7D%7D%29.%0A-%20auto_reconnect%20%7BBoolean%2C%20default%3Afalse%7D%2C%20reconnect%20on%20error.%0A-%20disableDriverBSONSizeCheck%20%7BBoolean%2C%20default%3Afalse%7D%2C%20force%20the%20server%20to%20error%20if%20the%20BSON%20message%20is%20to%20big%0A%0A%u6211%u4EEC%u5173%u6CE8%u7684%u9009%u9879%uFF0C%u6211%u7528%u7C97%u4F53%u505A%u4E86%u6807%u6CE8%u3002%0A%0A%u53E6%u5916%u6211%u4EEC%u5728%u524D%u6587%u4E2D%u63D0%u5230%uFF0C%u6709%u4E9B%u6587%u7AE0%u63A8%u8350%u7528generic-pool%u4E3Amongo%u8FDE%u63A5%u63D0%u4F9B%u8FDE%u63A5%u6C60%u3002%u4F46%u662F%uFF0C%u6211%u53D1%u73B0%60MongoClient.connect%60%u7684option%u91CC%u5C31%u6709poolSize%u3002%u5728%u5B9E%u9645%u9879%u76EE%u4E2D%u76F4%u63A5%u5C06%u4E4B%u8D4B%u4E3A100%uFF0C%u4E5F%u53EF%u4EE5%u5F97%u5230%u4E0D%u9519%u7684%u6548%u679C%u3002%0A

Edit

学习笔记 读书笔记 Programming

由于近日开发单片机程序的时候,写到一个小循环,不能确定其正确性,进而想到这本书的内容,算是一个小小的回顾。本书的读书笔记参考这篇The Science of Programming
正所谓温故而知新,这次重新摘读又有些新的收获。本想以QuickSort作为一个练习来运用本书的知识,结果尝试了许久,发现越陷越深。最后还是找了书上的例子(书中有QuickSort的例子)。本文最终目标是阐述如何推导QuickSort程序本身。先摘取一些重要的知识点,再利用这些知识点来推导QuickSort。

1. Quantification(量化)[原书4.2节]

  • There exists
    (E i: mi\<n: Ei)
  • For all
    (A i: mi\<n: Ei)
  • Numerial
    (N i: mi\<n: Ei)

他们有以下特性

(E i: mi\<k+1: Ei) = (E i: mi\<k: Ei) V Ek+1
(A i: mi\<n: Ei) = (E i: mi\<n: Ei)
(E i: mi\<n: Ei) = (N i: mi\<n: Ei) 1
(A i: mi\<n: Ei) = (N i: mi\<n: Ei) = n-m

2. 何为wp?[原书第7章]

原书的定义:

The Predicate Transformer wp

The set of all states such that execution of S begun in any one of them is guaranteed to terminate in a finite amount of time in a state satisfying R.

wp通常被用来表达一段程序

或者这样写

  • Q:前条件
  • S:执行代码(原文称为commands)
  • R:后条件

wp的特性

1) wp(S,F) = F
2) wp(S ^ Q) ^ wp(S,R) = wp(S,Q ^ R )
3) if Q then wp(S, Q ) wp(S, R)
4) wp(S wp(S, R)) wp(S ,Q v R)
5) wp(S, Q) V wp(S, R) = wp(S, Q V R) (for deterministic S)

3. 循环检查 (原书第11章)

定义

用数学语言标记一个循环如下:
{Q}
{inv P: the invariant}
{bound t: the bound function}
{init IS: initialization command}
do B1 S1

Bn Sn
od

Invariant

A predicate P that is true before and after each iteration of a loop.

这就是说P在任何情况下都要成立,于是有

  • Q wp(IS, P)
  • P wp(DO, P ^ BB)

Bound

又叫bound function

The function changes at each iteration; the relation remains invariantly true.
each iteration decreases t by at least one,so that termination is guaranteed to occur.

检查bound函数确保循环能结束。

Checklist

非常重要!!!

  1. Q wp(IS, P)
  2. P ^ Bi wp(Si, P)
  3. P ^ BB R
  4. P ^ BB (t > 0)
  5. P ^ Bi wp(“t1:=t; Si“, “t < t1“)

依次验证这5条,就能证明一个循环的正确性了

推测Command

通过后条件R,来推测执行代码(Command)。这就是所谓的Programming as a Goal-Oriented Activity。详情参看原书第14章。

Strategy for developing an alternative command: To invent a guarded command,find a command C whose execution will establish postcondition R in at least some cases; find a Boolean B satisfying Bwp(C,R) and put them together to form B C. Continue to invent guarded commands until the precondition of the construct implies that at least one guard is true. - 原文(14.7)

获得Guard(原书第15章)

所有的编程都应该是结果导向的(这与测试驱动开发TDD的思想暗合)。所以后条件R是最先有的。
其次,通过P BB R能得到BB(即guard)。虽然有公式AB <=> A V B=T,但是B的获得感觉更多是依靠分析而不是推导。具体例子会在本文最后推导Quick Sort中提及。
其中

不过由上式可知,此时的guard还是个总的guard,还需要将之分解为各个Bi

获得不变量Invariant(原书第16章)

书中给了4种方法:

  1. Delete a conjunct. For example, predicate A ^ B ^ C can be weakened to A ^ C
  2. Replace a constant by a variable. For example, predicate x b[1:10], where x is a simple variable,can be weakened to x b[1:i] ^ 110,where i is a fresh variable. Since a new
    variable has been introduced, its possible set of values must be precisely stated.
  3. Enlarge the range of a variable. For example, predicate 5 i < 10 can be weakened to 0 i < 10.
  4. Add a disjunct. For example, predicate A can be weakened to A v B, for some other predicate B.

书中推荐用前三个,根据阅读本书后面部分的经验,最常用的还是第二条。

推导Quick Sort (原书18.2节)

本文以推导快速排序作为结束。
快速排序的核心思想就是不断分割,在小数组中递归的作排序,直到小数组的元素个数少于或等于2个,即可进行直接排序了。所以分成Partition和Sort两部分。

Partition (原书练习16.5)

一次分割的后条件如下
R:

由R获得P

通过上一节中的方法二Replace a constant by a variable.来得到P。
P:

由P BB R推算BB

注意是推算,不是严格的逻辑推导
P (p-q+1=0) R
BB = (p-q+1=0)
BB = (pq-1)

推测command

由QuickSort的基本算法可知,有三个可能的command

  1. q++
  2. p–
  3. swap(b[p], b[q])
    所以现在程序形式如下:
    do pq-1
     if__ p:=p-1
      __ q:=q+1
      __ swap(b[p], b[q])
     fi
    od

由command+P+BB获得Guard

PBBwp(S,P)

  1. S = “p:=p-1”
      wp(S,P) =
  2. S = “q:=q+1”
    过程类似
      wp(S,P)=

对于前半部分的证明如下:
P中有
BB = (pq-1)
q < p q p-1 q+1p
得证
对于后半部分,显而易见需要b[q]x,即

  1. swap(b[p], b[q])
      wp(S,P)=

验证

Q: T
R:

  1. wp(IS, P) =
  2. PBB wp(,P)
    这个显然也是成立的,因为我们的就是这么来的
  3. PBBR
    同上,亦成立
  4. PBB(t>0)
    BB = (pq-1) p-q+10
    又由P有
    qp+1 p-q+10
    t=p-q+1>0
    得证
  5. P wp(“”, “t<t1")
    对于三种,t都显然会变小,所以满足

综上,5条check point都满足,程序正确性可保证

推导

根据快速排序算法可知,对数组排序,即进行若干次分割,直至子区间包含2个元素,即可以进行直接排序。

  • P: s is a set of pairs (i, j) representing disjoint array sections b[i:j] of b. Further,b[0:n—1] is ordered iff all the disjoint partitions given by set s are.
  • IS: s:={(0,n-1)}
  • R: b[0:n-1] are ordered
  • t: n - ordered

快速排序程序如下:
s:={(0,n-1)}
{Invariant: 如上P}
do s{} Choose((i,j),s); s:=s-{(i,j)};
   if j-i \< 2 Sort b[i:j] directly
    j-i 2 Partition(b,i,j,p;);
   fi
od

C代码

最终Quick Sort C代码实现如下:

1.#define N           50
2.#define MAX_SPLIT 50 // This should be logN
3.void swap(int* b, int p, int q)
4.
{
5. b[p]=b[p]+b[q];
6. b[q]=b[p]-b[q];
7. b[p]=b[p]-b[q];
8.}
9.int Partition (int* b, int m, int n)
10.
{
11. assert(n>m);
12. int p=n-1, q=m+1;
13. while (p != q-1)
14. {
15. if (b[p]>x)
16. {
17. p--;
18. }
19. else if (b[q]<=x)
20. {
21. q++;
22. }
23. else if (b[q]<=x && b[p]>x)
24. {
25. swap(b, p ,q);
26. p--;
27. q++;
28. }
29. }
30. return p;
31.}
32.void QuickSort(int* b, int n)
33.
{
34. typedef struct
35. {
36. int start;
37. int end;
38. }Pair s[MAX_SPLIT] = {0};
39. int count = 1; // how many pairs in s
40. s[0].start = 0;
41. s[0].end = n-1;
42. while (count > 0)
43. {
44. Pair p;
45. memcpy(&p, &s[count-1], sizeof(Pair));
46. if(p.end - p.start < 2)
47. {
48. if (p.start > p.end)
49. {
50. swap (b, p.start, p.end);
51. count --;
52. }
53. else
54. {
55. int m = p.start;
56. int n = p.end - p.start + 1;
57. int x = Partition(b, m, n);
58. s[count-1].start = p.start;
59. s[count-1].end = x-1;
60. s[count].start = x+1;
61. s[count].end = p.end;
62. count ++;
63. }
64. }
65. }
66.}
%23QuickSort%u63A8%u5BFC%20-%20The%20Science%20of%20Programming%0A%0A@%28%u5B66%u4E60%u7B14%u8BB0%29%5B%u8BFB%u4E66%u7B14%u8BB0%2C%20Programming%5D%0A%0A%u7531%u4E8E%u8FD1%u65E5%u5F00%u53D1%u5355%u7247%u673A%u7A0B%u5E8F%u7684%u65F6%u5019%uFF0C%u5199%u5230%u4E00%u4E2A%u5C0F%u5FAA%u73AF%uFF0C%u4E0D%u80FD%u786E%u5B9A%u5176%u6B63%u786E%u6027%uFF0C%u8FDB%u800C%u60F3%u5230%u8FD9%u672C%u4E66%u7684%u5185%u5BB9%uFF0C%u7B97%u662F%u4E00%u4E2A%u5C0F%u5C0F%u7684%u56DE%u987E%u3002%u672C%u4E66%u7684%u8BFB%u4E66%u7B14%u8BB0%u53C2%u8003%u8FD9%u7BC7%5BThe%20Science%20of%20Programming%5D%28https%3A//www.evernote.com/shard/s24/nl/697376807/c79122e8-c790-44b7-afa4-93eca3ad662e/%29%u3002%0A%u6B63%u6240%u8C13%u6E29%u6545%u800C%u77E5%u65B0%uFF0C%u8FD9%u6B21%u91CD%u65B0%u6458%u8BFB%u53C8%u6709%u4E9B%u65B0%u7684%u6536%u83B7%u3002%u672C%u60F3%u4EE5QuickSort%u4F5C%u4E3A%u4E00%u4E2A%u7EC3%u4E60%u6765%u8FD0%u7528%u672C%u4E66%u7684%u77E5%u8BC6%uFF0C%u7ED3%u679C%u5C1D%u8BD5%u4E86%u8BB8%u4E45%uFF0C%u53D1%u73B0%u8D8A%u9677%u8D8A%u6DF1%u3002%u6700%u540E%u8FD8%u662F%u627E%u4E86%u4E66%u4E0A%u7684%u4F8B%u5B50%uFF08%u4E66%u4E2D%u6709QuickSort%u7684%u4F8B%u5B50%uFF09%u3002%u672C%u6587%u6700%u7EC8%u76EE%u6807%u662F%u9610%u8FF0%u5982%u4F55%u63A8%u5BFCQuickSort%u7A0B%u5E8F%u672C%u8EAB%u3002%u5148%u6458%u53D6%u4E00%u4E9B%u91CD%u8981%u7684%u77E5%u8BC6%u70B9%2C%u518D%u5229%u7528%u8FD9%u4E9B%u77E5%u8BC6%u70B9%u6765%u63A8%u5BFCQuickSort%u3002%0A%0A%5BTOC%5D%0A%0A%23%23%u77E5%u8BC6%u70B9%u68B3%u7406%0A%23%23%231.%20Quantification%uFF08%u91CF%u5316%uFF09%5B%u539F%u4E664.2%u8282%5D%0A%0A*%20There%20exists%20%20%0A%09%28***E***%20i%3A%20m%24%5Cleq%24i%5C%3Cn%3A%20E%3Csub%3Ei%3C/sub%3E%29%0A*%20For%20all%20%20%0A%09%28***A***%20i%3A%20m%24%5Cleq%24i%5C%3Cn%3A%20E%3Csub%3Ei%3C/sub%3E%29%0A*%20Numerial%20%20%0A%09%28***N***%20i%3A%20m%24%5Cleq%24i%5C%3Cn%3A%20E%3Csub%3Ei%3C/sub%3E%29%0A%09%0A%u4ED6%u4EEC%u6709%u4EE5%u4E0B%u7279%u6027%0A%0A%3E%28***E***%20i%3A%20m%24%5Cleq%24i%5C%3Ck+1%3A%20E%3Csub%3Ei%3C/sub%3E%29%20%3D%20%28***E***%20i%3A%20m%24%5Cleq%24i%5C%3Ck%3A%20E%3Csub%3Ei%3C/sub%3E%29%20V%20E%3Csub%3Ek+1%3C/sub%3E%0A%3E%28***A***%20i%3A%20m%24%5Cleq%24i%5C%3Cn%3A%20E%3Csub%3Ei%3C/sub%3E%29%20%3D%20%24%5Cdaleth%24%28***E***%20i%3A%20m%24%5Cleq%24i%5C%3Cn%3A%20%24%5Cdaleth%24E%3Csub%3Ei%3C/sub%3E%29%0A%3E%28***E***%20i%3A%20m%24%5Cleq%24i%5C%3Cn%3A%20E%3Csub%3Ei%3C/sub%3E%29%20%3D%20%28***N***%20i%3A%20m%24%5Cleq%24i%5C%3Cn%3A%20E%3Csub%3Ei%3C/sub%3E%29%20%24%5Cgeq%24%201%0A%3E%28***A***%20i%3A%20m%24%5Cleq%24i%5C%3Cn%3A%20E%3Csub%3Ei%3C/sub%3E%29%20%3D%20%28***N***%20i%3A%20m%24%5Cleq%24i%5C%3Cn%3A%20E%3Csub%3Ei%3C/sub%3E%29%20%3D%20n-m%0A%0A%23%23%232.%20%u4F55%u4E3Awp%uFF1F%5B%u539F%u4E66%u7B2C7%u7AE0%5D%0A%u539F%u4E66%u7684%u5B9A%u4E49%uFF1A%0A%3EThe%20Predicate%20Transformer%20wp%0A%3E%0A%3EThe%20set%20of%20all%20states%20such%20that%20execution%20of%20**S**%20begun%20in%20any%20one%20of%20them%20%3Cfont%20color%3D%22Blue%22%3Eis%20guaranteed%20to%20terminate%20in%20a%20finite%20amount%20of%20time%3C/font%3E%20in%20a%20state%20satisfying%20**R**.%0A%0Awp%u901A%u5E38%u88AB%u7528%u6765%u8868%u8FBE%u4E00%u6BB5%u7A0B%u5E8F%0A%24%24%5C%7BQ%5C%7DS%5C%7BR%5C%7D%24%24%0A%u6216%u8005%u8FD9%u6837%u5199%0A%24%24Q%5CRightarrow%20wp%28S%2CR%29%24%24%0A-%20Q%3A%u524D%u6761%u4EF6%20%20%0A-%20S%3A%u6267%u884C%u4EE3%u7801%uFF08%u539F%u6587%u79F0%u4E3Acommands%uFF09%20%20%0A-%20R%3A%u540E%u6761%u4EF6%0A%0A**wp%u7684%u7279%u6027**%0A%3E1%29%20wp%28S%2CF%29%20%3D%20F%20%20%20%0A%3E2%29%20wp%28S%20%5E%20Q%29%20%5E%20wp%28S%2CR%29%20%3D%20wp%28S%2CQ%20%5E%20R%20%29%20%20%0A%3E3%29%20if%20Q%20then%20wp%28S%2C%20Q%20%29%20%24%5CRightarrow%24%20wp%28S%2C%20R%29%20%20%20%0A%3E4%29%20wp%28S%20wp%28S%2C%20R%29%29%20%24%5CRightarrow%24%20wp%28S%20%2CQ%20v%20R%29%20%20%0A%3E5%29%20wp%28S%2C%20Q%29%20V%20wp%28S%2C%20R%29%20%3D%20wp%28S%2C%20Q%20V%20R%29%20%28for%20deterministic%20S%29%0A%0A%23%23%233.%20%u5FAA%u73AF%u68C0%u67E5%20%28%u539F%u4E66%u7B2C11%u7AE0%29%0A%u5B9A%u4E49%20%20%0A%24%24%20wp%28DO%2C%20R%29%20%5CRightarrow%20%28E%20k%3A%200%5Cleq%20k%3A%20H_k%28R%29%29%24%24%0A%0A%u7528%u6570%u5B66%u8BED%u8A00%u6807%u8BB0%u4E00%u4E2A%u5FAA%u73AF%u5982%u4E0B%3A%20%20%0A%7BQ%7D%20%20%0A%7Binv%20P%3A%20the%20invariant%7D%20%20%0A%7Bbound%20t%3A%20the%20bound%20function%7D%20%20%0A%7Binit%20IS%3A%20initialization%20command%7D%20%20%0A**do**%20*B*%3Csub%3E1%3C/sub%3E%20%24%5Crightarrow%24%20*S*%3Csub%3E1%3C/sub%3E%20%20%0A%20%24%5Csquare%5C%20%5Ccdots%24%20%20%0A%20%24%5Csquare%5C%20%24%20*B*%3Csub%3En%3C/sub%3E%20%24%5Crightarrow%24%20*S*%3Csub%3En%3C/sub%3E%20%20%20%0A**od**%0A%0A%23%23%23%23Invariant%0A%3E%3Cfont%20%20face%3D%22Arial%22%3EA%20predicate%20**P**%20that%20is%20true%20**before%20and%20after%20each%20iteration**%20of%20a%20loop.%3C/font%3E%20%20%0A%0A%u8FD9%u5C31%u662F%u8BF4P%u5728%u4EFB%u4F55%u60C5%u51B5%u4E0B%u90FD%u8981%u6210%u7ACB%uFF0C%u4E8E%u662F%u6709%20%20%0A-%20Q%20%24%5CRightarrow%24%20wp%28IS%2C%20P%29%20%20%0A-%20P%20%24%5CRightarrow%24%20wp%28DO%2C%20P%20%5E%20%24%5Cdaleth%24BB%29%0A%0A%23%23%23%23Bound%0A%u53C8%u53EBbound%20function%0A%3E%3Cfont%20%20face%3D%22Arial%22%3EThe%20function%20changes%20at%20each%20iteration%3B%20the%20relation%20remains%20invariantly%20true.%0A%3E**each%20iteration%20decreases%20t%20by%20at%20least%20one%uFF0Cso%20that%20termination%20is%20guaranteed%20to%20occur.**%3C/font%3E%0A%0A%u68C0%u67E5bound%u51FD%u6570%u786E%u4FDD%u5FAA%u73AF%u80FD%u7ED3%u675F%u3002%0A%0A%23%23%23%23Checklist%0A%3Cfont%20color%3D%22IndianRed%22%20size%3D%226%22%3E%u975E%u5E38%u91CD%u8981%21%21%21%3C/font%3E%20%20%0A%3Ca%20name%3D%22checklist%22%3E%3C/a%3E%20%0A1.%20Q%20%24%5CRightarrow%24%20wp%28IS%2C%20P%29%0A2.%20P%20%5E%20B%3Csub%3Ei%3C/sub%3E%20%24%5CRightarrow%24%20wp%28S%3Csub%3Ei%3C/sub%3E%2C%20P%29%0A3.%20P%20%5E%20%24%5Cdaleth%24**BB**%20%24%5CRightarrow%24%20R%0A4.%20P%20%5E%20**BB**%20%24%5CRightarrow%24%20%28t%20%3E%200%29%0A5.%20P%20%5E%20B%3Csub%3Ei%3C/sub%3E%20%24%5CRightarrow%24%20wp%28%22t%3Csub%3E1%3C/sub%3E%3A%3Dt%3B%20S%3Csub%3Ei%3C/sub%3E%22%2C%20%22t%20%3C%20t%3Csub%3E1%3C/sub%3E%22%29%20%20%0A%0A%u4F9D%u6B21%u9A8C%u8BC1%u8FD95%u6761%uFF0C%u5C31%u80FD%u8BC1%u660E%u4E00%u4E2A%u5FAA%u73AF%u7684%u6B63%u786E%u6027%u4E86%0A%0A%23%23%234.%20%u5982%u4F55%u8FD0%u7528%u6240%u6709%u7684%u77E5%u8BC6%u6765%u5F00%u53D1%u4E00%u6BB5%u7A0B%u5E8F%uFF08%u539F%u4E66PART%20III%uFF09%0A%23%23%23%23%u63A8%u6D4BCommand%0A%u901A%u8FC7%u540E%u6761%u4EF6R%uFF0C%u6765%u63A8%u6D4B%u6267%u884C%u4EE3%u7801%28Command%29%u3002%u8FD9%u5C31%u662F%u6240%u8C13%u7684Programming%20as%20a%20Goal-Oriented%20Activity%u3002%u8BE6%u60C5%u53C2%u770B%u539F%u4E66%u7B2C14%u7AE0%u3002%0A%3E**Strategy%20for%20developing%20an%20alternative%20command**%3A%20To%20invent%20a%20guarded%20command%uFF0Cfind%20a%20command%20C%20whose%20execution%20will%20establish%20postcondition%20R%20in%20at%20least%20some%20cases%3B%20find%20a%20Boolean%20B%20satisfying%20B%24%5CRightarrow%24wp%28C%2CR%29%20and%20put%20them%20together%20to%20form%20B%20%24%5Crightarrow%24C.%20Continue%20to%20invent%20guarded%20commands%20until%20the%20precondition%20of%20the%20construct%20implies%20that%20at%20least%20one%20guard%20is%20true.%20-%20%u539F%u6587%2814.7%29%0A%0A%23%23%23%23%u83B7%u5F97Guard%uFF08%u539F%u4E66%u7B2C15%u7AE0%uFF09%0A%u6240%u6709%u7684%u7F16%u7A0B%u90FD%u5E94%u8BE5%u662F%u7ED3%u679C%u5BFC%u5411%u7684%uFF08%u8FD9%u4E0E%u6D4B%u8BD5%u9A71%u52A8%u5F00%u53D1TDD%u7684%u601D%u60F3%u6697%u5408%uFF09%u3002%u6240%u4EE5%u540E%u6761%u4EF6R%u662F%u6700%u5148%u6709%u7684%u3002%0A%u5176%u6B21%uFF0C%u901A%u8FC7P%20%24%5Cwedge%5C%20%5Cdaleth%24**BB**%20%24%5CRightarrow%24%20R%u80FD%u5F97%u5230**BB**%uFF08%u5373guard%uFF09%u3002%u867D%u7136%u6709%u516C%u5F0FA%24%5CRightarrow%24B%20%3C%3D%3E%20%24%5Cdaleth%24A%20V%20B%3DT%uFF0C%u4F46%u662FB%u7684%u83B7%u5F97%u611F%u89C9%u66F4%u591A%u662F%u4F9D%u9760%u5206%u6790%u800C%u4E0D%u662F%u63A8%u5BFC%u3002%u5177%u4F53%u4F8B%u5B50%u4F1A%u5728%u672C%u6587%u6700%u540E%u63A8%u5BFCQuick%20Sort%u4E2D%u63D0%u53CA%u3002%0A%u5176%u4E2D%0A%24%24%20%5Cmathbf%20B%20%5Cmathbf%20B%20%3D%20B_1%20%5Cwedge%20%5C%20B%5C_2%20%5Cwedge%20%5Cldots%20%5C%20B%5C_n%24%24%0A%u4E0D%u8FC7%u7531%u4E0A%u5F0F%u53EF%u77E5%uFF0C%u6B64%u65F6%u7684guard%u8FD8%u662F%u4E2A%u603B%u7684guard%uFF0C%u8FD8%u9700%u8981%u5C06%u4E4B%u5206%u89E3%u4E3A%u5404%u4E2AB%3Csub%3Ei%3C/sub%3E%u3002%0A%23%23%23%23%u83B7%u5F97%u4E0D%u53D8%u91CFInvariant%uFF08%u539F%u4E66%u7B2C16%u7AE0%uFF09%0A%u4E66%u4E2D%u7ED9%u4E864%u79CD%u65B9%u6CD5%uFF1A%20%20%0A1.%20**Delete%20a%20conjunct.**%20For%20example%2C%20predicate%20A%20%5E%20B%20%5E%20C%20can%20be%20weakened%20to%20A%20%5E%20C%20%20%0A2.%20**Replace%20a%20constant%20by%20a%20variable.**%20For%20example%2C%20predicate%20x%20%24%5Cleq%24b%5B1%3A10%5D%2C%20where%20x%20is%20a%20simple%20variable%uFF0Ccan%20be%20weakened%20to%20x%20%24%5Cleq%24%20b%5B1%3Ai%5D%20%5E%201%24%5Cleq%20i%20%5Cleq%2410%uFF0Cwhere%20i%20is%20a%20fresh%20variable.%20Since%20a%20new%0Avariable%20has%20been%20introduced%2C%20%3Cfont%20color%3D%22IndianRed%22%3Eits%20possible%20set%20of%20values%20must%20be%20precisely%20stated.%3C/font%3E%20%20%0A3.%20**Enlarge%20the%20range%20of%20a%20variable.**%20For%20example%2C%20predicate%205%20%24%5Cleq%24%20i%20%3C%2010%20can%20be%20weakened%20to%200%20%24%5Cleq%24%20i%20%3C%2010.%20%20%0A4.%20**Add%20a%20disjunct.**%20For%20example%2C%20predicate%20A%20can%20be%20weakened%20to%20A%20v%20B%2C%20for%20some%20other%20predicate%20B.%0A%0A%u4E66%u4E2D%u63A8%u8350%u7528%u524D%u4E09%u4E2A%uFF0C%u6839%u636E%u9605%u8BFB%u672C%u4E66%u540E%u9762%u90E8%u5206%u7684%u7ECF%u9A8C%uFF0C%u6700%u5E38%u7528%u7684%u8FD8%u662F%u7B2C%u4E8C%u6761%u3002%0A%0A%23%23%u63A8%u5BFCQuick%20Sort%20%uFF08%u539F%u4E6618.2%u8282%uFF09%0A%u672C%u6587%u4EE5%u63A8%u5BFC%u5FEB%u901F%u6392%u5E8F%u4F5C%u4E3A%u7ED3%u675F%u3002%0A%u5FEB%u901F%u6392%u5E8F%u7684%u6838%u5FC3%u601D%u60F3%u5C31%u662F%u4E0D%u65AD%u5206%u5272%uFF0C%u5728%u5C0F%u6570%u7EC4%u4E2D%u9012%u5F52%u7684%u4F5C%u6392%u5E8F%uFF0C%u76F4%u5230%u5C0F%u6570%u7EC4%u7684%u5143%u7D20%u4E2A%u6570%u5C11%u4E8E%u6216%u7B49%u4E8E2%u4E2A%uFF0C%u5373%u53EF%u8FDB%u884C%u76F4%u63A5%u6392%u5E8F%u4E86%u3002%u6240%u4EE5%u5206%u6210Partition%u548CSort%u4E24%u90E8%u5206%u3002%0A%23%23%23Partition%20%uFF08%u539F%u4E66%u7EC3%u4E6016.5%uFF09%0A%u4E00%u6B21%u5206%u5272%u7684%u540E%u6761%u4EF6%u5982%u4E0B%0AR%3A%20%24m%5Cleq%20p%3Cn%20%5Cwedge%20perm%28b%2C%20B%29%20%5Cwedge%20b%24%21%5BAlt%20text%5D%28./1453770826140.png%29%0A%23%23%23%23%u7531R%u83B7%u5F97P%0A%u901A%u8FC7%u4E0A%u4E00%u8282%u4E2D%u7684%u65B9%u6CD5%u4E8C**Replace%20a%20constant%20by%20a%20variable.**%u6765%u5F97%u5230P%u3002%0AP%3A%20%24m%3Cq%20%5Cleq%20p+1%20%5Cleq%20n%20%5Cwedge%20x%3DB%5Bm%5D%20%5Cwedge%20%24%21%5BAlt%20text%5D%28./1453770875251.png%29%0A%23%23%23%23%u7531P%20%24%5Cwedge%20%5Cdaleth%24**BB**%20%24%5CRightarrow%24R%3Cfont%20color%3D%22IndianRed%22%3E%u63A8%u7B97%3C/font%3EBB%0A%u6CE8%u610F%u662F%u63A8%u7B97%uFF0C%u4E0D%u662F%u4E25%u683C%u7684%u903B%u8F91%u63A8%u5BFC%0A%24%5Cbecause%5C%20%24P%20%24%5Cwedge%24%20%28p-q+1%3D0%29%20%24%5CLeftrightarrow%24%20R%0A%24%5Ctherefore%5C%20%5Cdaleth%24**BB**%20%3D%20%28p-q+1%3D0%29%0A%24%5Ctherefore%5C%20%24**BB**%20%3D%20%28p%24%5Cneq%24q-1%29%0A%23%23%23%23Bound%20function%0At%20%3D%20p-q+1%0A%23%23%23%23%u63A8%u6D4Bcommand%0A%u7531QuickSort%u7684%u57FA%u672C%u7B97%u6CD5%u53EF%u77E5%uFF0C%u6709%u4E09%u4E2A%u53EF%u80FD%u7684command%0A1.%20q++%0A2.%20p--%0A3.%20swap%28b%5Bp%5D%2C%20b%5Bq%5D%29%0A%u6240%u4EE5%u73B0%u5728%u7A0B%u5E8F%u5F62%u5F0F%u5982%u4E0B%uFF1A%0Ado%20p%24%5Cneq%24q-1%0A%u3000if________%20%24%5Crightarrow%24%20p%3A%3Dp-1%0A%u3000%24%5Csquare%24%20________%20%24%5Crightarrow%24%20q%3A%3Dq+1%0A%u3000%24%5Csquare%24%20________%20%24%5Crightarrow%24%20swap%28b%5Bp%5D%2C%20b%5Bq%5D%29%0A%u3000fi%0Aod%0A%23%23%23%23%u7531command+P+BB%u83B7%u5F97Guard%0AP%24%5Cwedge%24**BB**%24%5Cwedge%20B_i%20%5CRightarrow%24wp%28S%2CP%29%0A1.%20S%20%3D%20%22p%3A%3Dp-1%22%0A%u3000%u3000wp%28S%2CP%29%20%3D%20%24%28m%3Cq%20%5Cleq%20p%20%5Cleq%20n%29%20%5Cwedge%20%28x%3DB%5Bm%5D%29%20%5Cwedge%20%24%21%5BAlt%20text%5D%28./1453770917851.png%29%0A%24%5Cbecause%24**BB**%20%3D%20%28p%24%5Cneq%24q-1%29%0A%24%5Ctherefore%24q%5C%3Cp+1%0A%24%5Ctherefore%24q%24%5Cleq%24p%0A%u53EF%u89C1%uFF0C%u8981%u8BC1%u51FAP%24%5Cwedge%24**BB**%24%5Cwedge%20B_i%20%5CRightarrow%24wp%28S%2CP%29%u53EA%u8981%u56FE%u5F62%u4E2D%u7684b%5Bp%5D%u80FD%u6EE1%u8DB3%u5206%u5272%u6761%u4EF6%u5C31%u53EF%u4EE5%u4E86%uFF0C%u6240%u4EE5%u6709%0A%24%24B_i%20%3D%20b%5Bp%5D%3Ex%24%24%0A2.%20S%20%3D%20%22q%3A%3Dq+1%22%0A%u8FC7%u7A0B%u7C7B%u4F3C%0A%u3000%u3000wp%28S%2CP%29%3D%24m%3Cq+1%5Cleq%20p+1%20%5Cleq%20n%20%5Cwedge%24%21%5BAlt%20text%5D%28./1453770962336.png%29%0A%0A%u5BF9%u4E8E%u524D%u534A%u90E8%u5206%u7684%u8BC1%u660E%u5982%u4E0B%uFF1A%0AP%u4E2D%u6709%24m%3Cq%20%5Cleq%20p+1%20%5Cleq%20n%24%0A**BB**%20%3D%20%28p%24%5Cneq%24q-1%29%0A%24%5Ctherefore%24%20q%20%3C%20p%20%24%5CLeftrightarrow%24%20q%20%24%5Cleq%24p-1%20%24%5CLeftrightarrow%24%20q+1%24%5Cleq%24p%0A%u5F97%u8BC1%0A%u5BF9%u4E8E%u540E%u534A%u90E8%u5206%uFF0C%u663E%u800C%u6613%u89C1%u9700%u8981b%5Bq%5D%24%5Cleq%24x%2C%u5373%0A%24%24B_i%20%3D%20%28b%5Bq%5D%20%5Cleq%20x%29%24%24%0A3.%20swap%28b%5Bp%5D%2C%20b%5Bq%5D%29%0A%u3000%u3000wp%28S%2CP%29%3D%24m%3Cq%20%5Cleq%20p+1%20%5Cleq%20n%20%5Cwedge%24%21%5BAlt%20text%5D%28./1453771153446.png%29%0A%u4ECD%u7136%u663E%u800C%u6613%u89C1%u6709%uFF1A%0A%24%24B_i%20%3D%20b%5Bq%5D%20%5Cleq%20x%20%5Cwedge%20b%5Bp%5D%3Ex%24%24%0A%u4F9D%u6B21%3Cfont%20color%3D%22IndianRed%22%20size%3D%225%22%3E%u5206%u5272%u7A0B%u5E8F%3C/font%3E%u5199%u4F5C%uFF1A%0A**do**%20p%24%5Cneq%24q-1%0A%u3000**if**%20b%5Bp%5D%5C%3Ex%20%24%5Crightarrow%24%20p%3A%3Dp-1%0A%u3000%24%5Csquare%24%20b%5Bq%5D%24%5Cleq%24%20x%20%24%5Crightarrow%24%20q%3A%3Dq+1%0A%u3000%24%5Csquare%24%20%28b%5Bq%5D%24%5Cleq%24%20x%29%20%24%5Cwedge%24%20%28b%5Bp%5D%5C%3Ex%29%24%5Crightarrow%24%20swap%28b%5Bp%5D%2C%20b%5Bq%5D%29%3B%20p%2Cq%3A%3Dp-1%2Cq+1%0A%u3000**fi**%0A**od**%0A%23%23%23%23%u9A8C%u8BC1%0AQ%3A%20T%0AR%3A%20%24m%5Cleq%20p%20%5Cleq%20n%20%5Cwedge%20perm%28b%2C%20B%29%20%5Cwedge%20b%24%21%5BAlt%20text%5D%28./1453771195972.png%29%0AP%3A%20%24m%3Cq%20%5Cleq%20p+1%20%5Cleq%20n%20%5Cwedge%20x%3DB%5Bm%5D%20%5Cwedge%20%24%21%5BAlt%20text%5D%28./1453771223774.png%29%0ABound%3A%20t%20%3D%20p-q+1%0AIS%3A%20p%3A%3Dn-1%2C%20q%3A%3Dm+1%0A%u6839%u636E5%u6761%5BCheck%20List%5D%28%23checklist%29%uFF0C%u4F9D%u6B21check%uFF1A%0A1.%20wp%28IS%2C%20P%29%20%3D%20%24m%3Cm+1%5Cleq%20n%20%5Cwedge%20%28x%3DB%5Bm%5D%29%20%5Cwedge%20%24%21%5BAlt%20text%5D%28./1453771263078.png%29%0A%u663E%u7136%u662F%u6210%u7ACB%u7684%0A2.%20P%24%5Cwedge%24**BB**%24%5Cwedge%20B_i%20%5CRightarrow%24%20wp%28%24S_i%24%2CP%29%0A%u8FD9%u4E2A%u663E%u7136%u4E5F%u662F%u6210%u7ACB%u7684%uFF0C%u56E0%u4E3A%u6211%u4EEC%u7684%24B_i%24%u5C31%u662F%u8FD9%u4E48%u6765%u7684%0A3.%20P%24%5Cwedge%20%5Cdaleth%24**BB**%24%5CRightarrow%24R%0A%u540C%u4E0A%uFF0C%u4EA6%u6210%u7ACB%0A4.%20P%24%5Cwedge%24**BB**%24%5CRightarrow%24%28t%5C%3E0%29%0A**BB**%20%3D%20%28p%24%5Cneq%24q-1%29%20%24%5CLeftrightarrow%24%20p-q+1%24%5Cneq%240%0A%u53C8%u7531P%u6709%0Aq%24%5Cleq%24p+1%20%24%5CLeftrightarrow%24%20p-q+1%24%5Cgeq%240%0A%24%5Ctherefore%24%20t%3Dp-q+1%3E0%0A%u5F97%u8BC1%0A5.%20P%24%5Cwedge%20B_i%20%5CRightarrow%24%20wp%28%22%24t_1%3Dt%3BS_i%24%22%2C%20%22t%3Ct1%22%29%0A%u5BF9%u4E8E%u4E09%u79CD%24S_i%24%2Ct%u90FD%u663E%u7136%u4F1A%u53D8%u5C0F%uFF0C%u6240%u4EE5%u6EE1%u8DB3%0A%0A%u7EFC%u4E0A%uFF0C5%u6761check%20point%u90FD%u6EE1%u8DB3%uFF0C%u7A0B%u5E8F%u6B63%u786E%u6027%u53EF%u4FDD%u8BC1%0A%23%23%23Sort%0A%23%23%23%23%u63A8%u5BFC%0A%u6839%u636E%u5FEB%u901F%u6392%u5E8F%u7B97%u6CD5%u53EF%u77E5%uFF0C%u5BF9%u6570%u7EC4%u6392%u5E8F%uFF0C%u5373%u8FDB%u884C%u82E5%u5E72%u6B21%u5206%u5272%uFF0C%u76F4%u81F3%u5B50%u533A%u95F4%u5305%u542B%24%5Cleq%242%u4E2A%u5143%u7D20%uFF0C%u5373%u53EF%u4EE5%u8FDB%u884C%u76F4%u63A5%u6392%u5E8F%u3002%0A*%20P%3A%20*s*%20is%20a%20set%20of%20pairs%20%28i%2C%20j%29%20representing%20disjoint%20array%20sections%20b%5Bi%3Aj%5D%20of%20b.%20Further%uFF0Cb%5B0%3An%u20141%5D%20is%20ordered%20iff%20all%20the%20disjoint%20partitions%20given%20by%20set%20*s*%20are.%0A*%20IS%3A%20*s*%3A%3D%7B%280%2Cn-1%29%7D%0A*%20R%3A%20b%5B0%3An-1%5D%20are%20ordered%0A*%20t%3A%20n%20-%20ordered%0A%0A%u5FEB%u901F%u6392%u5E8F%u7A0B%u5E8F%u5982%u4E0B%3A%0A*s*%3A%3D%7B%280%2Cn-1%29%7D%0A%7B*Invariant*%3A%20%u5982%u4E0AP%7D%0A**do**%20*s*%24%5Cneq%24%7B%7D%20%24%5Crightarrow%24%20Choose%28%28i%2Cj%29%2C*s*%29%3B%20s%3A%3Ds-%7B%28i%2Cj%29%7D%3B%0A%u3000%u3000%u3000**if**%20j-i%20%5C%3C%202%20%24%5Crightarrow%24%20Sort%20b%5Bi%3Aj%5D%20directly%0A%u3000%u3000%u3000%24%5Csquare%24%20j-i%20%24%5Cgeq%242%20%24%5Crightarrow%24%20Partition%28b%2Ci%2Cj%2Cp%3B%29%3B%20%24s%3A%3Ds%5Ccup%20%7B%28i%2Cp-1%29%7D%20%5Ccup%20%7B%28p+1%2Cj%29%7D%24%0A%u3000%u3000%u3000**fi**%0A**od**%0A%23%23%23%23C%u4EE3%u7801%0A%u6700%u7EC8Quick%20Sort%20C%u4EE3%u7801%u5B9E%u73B0%u5982%u4E0B%3A%0A%60%60%60c%0A%23define%20N%09%09%0950%0A%23define%20MAX_SPLIT%20%0950%20//%20This%20should%20be%20logN%0Avoid%20swap%28int*%20b%2C%20int%20p%2C%20int%20q%29%0A%7B%0A%09b%5Bp%5D%3Db%5Bp%5D+b%5Bq%5D%3B%0A%09b%5Bq%5D%3Db%5Bp%5D-b%5Bq%5D%3B%0A%09b%5Bp%5D%3Db%5Bp%5D-b%5Bq%5D%3B%0A%7D%0Aint%20Partition%20%28int*%20b%2C%20int%20m%2C%20int%20n%29%0A%7B%0A%09assert%28n%3Em%29%3B%0A%09int%20p%3Dn-1%2C%20q%3Dm+1%3B%0A%09while%20%28p%20%21%3D%20q-1%29%0A%09%7B%0A%09%09if%20%28b%5Bp%5D%3Ex%29%0A%09%09%7B%0A%09%09%09p--%3B%0A%09%09%7D%0A%09%09else%20if%20%28b%5Bq%5D%3C%3Dx%29%0A%09%09%7B%0A%09%09%09q++%3B%0A%09%09%7D%0A%09%09else%20if%20%28b%5Bq%5D%3C%3Dx%20%26%26%20b%5Bp%5D%3Ex%29%0A%09%09%7B%0A%09%09%09swap%28b%2C%20p%20%2Cq%29%3B%0A%09%09%09p--%3B%0A%09%09%09q++%3B%0A%09%09%7D%0A%09%7D%0A%09return%20p%3B%0A%7D%0Avoid%20QuickSort%28int*%20b%2C%20int%20n%29%0A%7B%0A%09typedef%20struct%20%0A%09%7B%0A%09%09int%20start%3B%0A%09%09int%20end%3B%0A%09%7DPair%20s%5BMAX_SPLIT%5D%20%3D%20%7B0%7D%3B%0A%09int%20count%20%3D%201%3B%20//%20how%20many%20pairs%20in%20s%0A%09s%5B0%5D.start%20%3D%200%3B%0A%09s%5B0%5D.end%20%3D%20n-1%3B%0A%09while%20%28count%20%3E%200%29%0A%09%7B%0A%09%09Pair%20p%3B%0A%09%09memcpy%28%26p%2C%20%26s%5Bcount-1%5D%2C%20sizeof%28Pair%29%29%3B%0A%09%09if%28p.end%20-%20p.start%20%3C%202%29%0A%09%09%7B%0A%09%09%09if%20%28p.start%20%3E%20p.end%29%0A%09%09%09%7B%0A%09%09%09%09swap%20%28b%2C%20p.start%2C%20p.end%29%3B%0A%09%09%09%09count%20--%3B%0A%09%09%09%7D%0A%09%09%09else%0A%09%09%09%7B%0A%09%09%09%09int%20m%20%3D%20p.start%3B%0A%09%09%09%09int%20n%20%3D%20p.end%20-%20p.start%20+%201%3B%0A%09%09%09%09int%20x%20%3D%20Partition%28b%2C%20m%2C%20n%29%3B%0A%09%09%09%09s%5Bcount-1%5D.start%20%3D%20p.start%3B%0A%09%09%09%09s%5Bcount-1%5D.end%20%3D%20x-1%3B%0A%09%09%09%09s%5Bcount%5D.start%20%3D%20x+1%3B%0A%09%09%09%09s%5Bcount%5D.end%20%3D%20p.end%3B%0A%09%09%09%09count%20++%3B%0A%09%09%09%7D%0A%09%09%7D%20%0A%09%7D%0A%7D%0A%60%60%60%0A%0A%0A%0A%0A%0A%0A

@(学习笔记)[读书笔记, Programming]

由于近日开发单片机程序的时候,写到一个小循环,不能确定其正确性,进而想到这本书的内容,算是一个小小的回顾。本书的读书笔记参考这篇The Science of Programming
正所谓温故而知新,这次重新摘读又有些新的收获。本想以QuickSort作为一个练习来运用本书的知识,结果尝试了许久,发现越陷越深。最后还是找了书上的例子(书中有QuickSort的例子)。本文最终目标是阐述如何推导QuickSort程序本身。先摘取一些重要的知识点,再利用这些知识点来推导QuickSort。

知识点梳理

1. Quantification(量化)[原书4.2节]

Read more »

Edit


Markdown的书写工具多如牛毛,但是实现方式不尽相同,有些支持LaTex有些不支持。支持者如马克飞象,CMD工作站;不支持者如我正在用的MacDown。不过没关系,因为Markdown与HTML的天生联系,我们可以很好的内嵌MathJax来解决这个问题。

MathJax

官方文档说要将下面这个标签加到网页的中,不过我们加到中使用效果也是一样

1.<script type="text/javascript"
2. src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML.js">

3.
</script>

检验一下效果

  • 行级:
    $\daleth$
    - Science of Programming中常用的符号,表示取反
  • 块级
    $$bla..bla..bla$$

另外一点还需要说明的是,Markdown中行内内嵌数学公式的标记是$...$,而MathJax默认并不支持该标记,官方的说法是

That is because dollar signs appear too often in non-mathematical settings, which could cause some text to be treated as mathematics unexpectedly.

MathJax中默认的行内内嵌公式标记是\(...\),写作\\(...\\),例如\\(\daleth\\)加上下面这段代码就能让MathJax支持$...$

1.<script type="text/x-mathjax-config"> MathJax.Hub.Config({ 
2. tex2jax: {inlineMath: [['$','$'], ['\\(','\\)']]} });
</script>
3.<script type="text/javascript" src="path-to-mathjax/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>

而马克飞象中的MathJax可能经过定制,是默认支持$...$内嵌的

%3Cscript%20type%3D%22text/javascript%22%0A%09%20src%3D%22https%3A//cdn.mathjax.org/mathjax/latest/MathJax.js%3Fconfig%3DTeX-AMS-MML_HTMLorMML.js%22%3E%0A%3C/script%3E%0A%5BTOC%5D%0A%23Markdown%u5BF9%u6570%u5B66%u516C%u5F0F%u7684%u652F%u6301%0A@%28%u5B66%u4E60%u7B14%u8BB0%29%5BMarkdown%5D%0AMarkdown%u7684%u4E66%u5199%u5DE5%u5177%u591A%u5982%u725B%u6BDB%uFF0C%u4F46%u662F%u5B9E%u73B0%u65B9%u5F0F%u4E0D%u5C3D%u76F8%u540C%uFF0C%u6709%u4E9B%u652F%u6301LaTex%u6709%u4E9B%u4E0D%u652F%u6301%u3002%u652F%u6301%u8005%u5982%u9A6C%u514B%u98DE%u8C61%uFF0CCMD%u5DE5%u4F5C%u7AD9%uFF1B%u4E0D%u652F%u6301%u8005%u5982%u6211%u6B63%u5728%u7528%u7684MacDown%u3002%u4E0D%u8FC7%u6CA1%u5173%u7CFB%uFF0C%u56E0%u4E3AMarkdown%u4E0EHTML%u7684%u5929%u751F%u8054%u7CFB%uFF0C%u6211%u4EEC%u53EF%u4EE5%u5F88%u597D%u7684%u5185%u5D4C%5BMathJax%5D%28http%3A//docs.mathjax.org/en/latest/start.html%23mathjax-cdn%29%u6765%u89E3%u51B3%u8FD9%u4E2A%u95EE%u9898%u3002%0A%0A%23%23MathJax%0A%u5B98%u65B9%u6587%u6863%u8BF4%u8981%u5C06%u4E0B%u9762%u8FD9%u4E2A%u6807%u7B7E%u52A0%u5230%u7F51%u9875%u7684%3Chead%3E%u4E2D%uFF0C%u4E0D%u8FC7%u6211%u4EEC%u52A0%u5230%3Cbody%3E%u4E2D%u4F7F%u7528%u6548%u679C%u4E5F%u662F%u4E00%u6837%0A%09%0A%09%3Cscript%20type%3D%22text/javascript%22%0A%09%20%20src%3D%22https%3A//cdn.mathjax.org/mathjax/latest/MathJax.js%3Fconfig%3DTeX-AMS-MML_HTMLorMML.js%22%3E%0A%09%3C/script%3E%0A%09%0A%09%0A%u68C0%u9A8C%u4E00%u4E0B%u6548%u679C%0A%0A*%20%u884C%u7EA7%3A%20%0A%20%20%20%20%5C%24%5Cdaleth%5C%24%0A%24%5Cdaleth%24%20-%20Science%20of%20Programming%u4E2D%u5E38%u7528%u7684%u7B26%u53F7%uFF0C%u8868%u793A%u53D6%u53CD%0A*%20%u5757%u7EA7%0A%20%20%20%20%5C%24%5C%24bla..bla..bla%5C%24%5C%24%0A%24%24x%3D%5Cfrac%7B-b%5Cpm%5Csqrt%7Bb%5E2-4ac%7D%7D%7B2a%7D%24%24%0A%0A%u53E6%u5916%u4E00%u70B9%u8FD8%u9700%u8981%u8BF4%u660E%u7684%u662F%uFF0CMarkdown%u4E2D%u884C%u5185%u5185%u5D4C%u6570%u5B66%u516C%u5F0F%u7684%u6807%u8BB0%u662F%60%24...%24%60%uFF0C%u800CMathJax%u9ED8%u8BA4%u5E76%u4E0D%u652F%u6301%u8BE5%u6807%u8BB0%uFF0C%u5B98%u65B9%u7684%u8BF4%u6CD5%u662F%0A%3EThat%20is%20because%20dollar%20signs%20appear%20too%20often%20in%20non-mathematical%20settings%2C%20which%20could%20cause%20some%20text%20to%20be%20treated%20as%20mathematics%20unexpectedly.%0A%0AMathJax%u4E2D%u9ED8%u8BA4%u7684%u884C%u5185%u5185%u5D4C%u516C%u5F0F%u6807%u8BB0%u662F%60%5C%28...%5C%29%60%uFF0C%u5199%u4F5C%60%5C%5C%28...%5C%5C%29%60%uFF0C%u4F8B%u5982%60%5C%5C%28%5Cdaleth%5C%5C%29%60%u52A0%u4E0A%u4E0B%u9762%u8FD9%u6BB5%u4EE3%u7801%u5C31%u80FD%u8BA9MathJax%u652F%u6301%60%24...%24%60%u4E86%0A%60%60%60xml%0A%3Cscript%20type%3D%22text/x-mathjax-config%22%3E%20MathJax.Hub.Config%28%7B%20%0A%20%20%20%20tex2jax%3A%20%7BinlineMath%3A%20%5B%5B%27%24%27%2C%27%24%27%5D%2C%20%5B%27%5C%5C%28%27%2C%27%5C%5C%29%27%5D%5D%7D%20%7D%29%3B%20%3C/script%3E%20%0A%3Cscript%20type%3D%22text/javascript%22%20src%3D%22path-to-mathjax/MathJax.js%3Fconfig%3DTeX-AMS-MML_HTMLorMML%22%3E%3C/script%3E%0A%60%60%60%0A%0A%u800C%u9A6C%u514B%u98DE%u8C61%u4E2D%u7684MathJax%u53EF%u80FD%u7ECF%u8FC7%u5B9A%u5236%uFF0C%u662F%u9ED8%u8BA4%u652F%u6301%60%24...%24%60%u5185%u5D4C%u7684%0A

Chp 1. Propositions
conjunction: b ^ c
disjunction: b V c
implication: b => c

1.4 Precedence Rules for Operators
The order of evaluation of different, adjacent operators is given by the list: not (has highest precedence and binds tightest), and, or, imp,equals

1.6 Propositions as Sets of States
A proposition represents, or describes,the set of states in which it is true.

Weak & Strong
Proposition b is weaker than c if c =>b. Correspondingly, c is said to be stronger than b. The weakest proposition is T (or any tautology), because it represents the set of all states; the strongest is F.

Chp 2. Reasoning using Equivalence Transformations
An equivalence is an equality that is a tautology.

12条law,所有证明的基础
1. Commutative Laws (These allow us to reorder the operands of and, or and equality):
(E1 ^ E2) = (E2 ^ E1)
(E1 v E2) = (E2 v E1)
(E1 = E2 ) = (E2 = E1)
2. Associative Laws (These allow us to dispense with parentheses when dealing with sequences of and and sequences of or):
E1 ^ (E2 ^ E3) = (E1 ^ E2)^E3 (so write both as E1^E2^E3)
E1 v (E2 v E3) = ( E1 v E2 ) v E3
3. Distributive Laws (These are useful in factoring a proposition, in the same way that we rewrite 2*(3+4) as (2*3)+(2*4)):
E1 v ( E2 ^ E3 ) = (E1 v E2) ^ (E1 v E3 )
E1 ^ (E2 v E3) = ( E1 ^ E 2 ) v ( E1 ^ E3 )
4. De Morgan’s Laws (After Augustus De Morgan, a 19th century English mathematician who,along with Boole,laid much of the founda­tions for mathematical logic):
┐(E1 ^ E2 ) = ┐E1 v ┐E2
┐(E1 v E2) = ┐E1 ^ ┐E2
5. Law of Negation: ┐(┐E1) = E1
6. Law of the Excluded Middle: E1 v ┐E1 = T
7. Law of Contradiction: E1 ^ ┐El = F
8. Law of Implication: E1 =>E2 = ┐E1 v E2
9. Law of Equality: (El = E2) = (E1 => E2)^(E2=>E1)
10. Laws of or-simplification:
E1 v E1 = E1
E1 v T = T
E1 v F = E1
E1 v (E1 ^ E2 ) = El
11. Laws of and-simplification:
E1 ^ E1 = E1
E1 ^ T = E1
E1 ^ F = F
E1 ^ (E1 v E2) = E1
12. Law of Identity: E1 = E1
比较不容易想到的是第8条和第9条,要牢记,之后的证明会经常用到。另外从上面的基本定理中还可以推导出以下的引理。
重要引理:(E1=>E2) => (E1^E2=>E1)
证明:1) E1^E2=>E1=T 2) E1=>E1^E2=T
= ┓(E1^E2)VE2 = ┓E1V(E1^E2)
= ┓E1V┓E2VE2 = (┓E1VE1)^(┓E1VE2)
= T = T ^ T = T
这个不是那么容易能看出来,所以给出证明。这个推导表示: 如果E1=>E2,表示E1是E2的一个子集

Chp 3. A Natural Deduction System (skip)

Chp 4. Predicates

cand & cor
The operands of these new operators can be any of three values: F, T and U (for Undefined).
cand/cor 支持以下定理
2. Associativity:
E1 cand (E2 cand E3) = (E1 cand E2) cand E3
E1 cor (E2 cor E3) = (E1 cor E2) cor E3
3. Distributivity:
E1 cand (E2 cor E3) = (E1 cand E2) cor (E1 cand E3)
E1 cor (E2 cand E3) = (E1 cor E2) cand (E1 cor E3)
4. De Morgan:
┐(E1 cand E2) = ┐E1 cor ┐E2
┐(El cor E2) = ┐E1 cand ┐E2
6. Excluded Middle: E1 cor ┐E1 = T (provided E1 is well-defined)
7. Contradiction: E1 cand ┐E1 = F (provided E1 is well-defined)
10. cor-simplification
E1 cor E1 = E1
E1 cor T = T (provided El is well-defined)
E1 cor F = E1
E1 cor (E1 cand E2) = E1
11. cand-simplification
E1 cand E1 = E1
E1 cand T = E1
E1 cand F = F (provided El is well-defined)
E1 cand (E1 cor E2) = E1

4.2 Quantification
summarize: (∑: i: 0<i<n:xi)
连乘积: (∏: i: 0<i<n:xi)
exist one: (E: i: 0<i<n: xi)
for all: (A: i: 0<i<n: xi)

free & bound
(A i:m ( i < n : x*i >0)
m, n and x are free identifiers of the predicate. Identif­ier i is bound.

4.4 Textual Substitution
  • 什么叫bound字符?看下面的例子
But this is not the desired predicate,because the i in y —i has become  bound to the quantifier A, since it now occurs within the scope of A.
要这样替换,把bound字符先替换成其他的字符,以免造成confuse。

Chp 5. Notations and Conventions for Arrays
Array b is considered to be a (partial) function: b is a simple variable that contains a function from subscript values to integers.

However, when developing a program to meet the specification it is often advantageous to expand the abbrevia­tions into their full form,because the full form can give more insight into program development.

Chp 6. Using Assertions To Document Programs
{Q}S{R}
Q is called the precondition or input assertion of S; R the postcondition, output assertion or result assertion.

6.3 Proof Outlines
A predicate placed in a program is called an assertion, we assert it is true at that point of execution. A program together with an assertion between each pair of statements is called a proof outline.

Part II The Semantics of a Small Language

Chapter 7 The Predicate Transformer wp
wp( S, R)
The set of all states such that execution of S begun in any one of them is guaranteed to terminate in a finite amount of time in a state satisfying R.
We call wp( S, R ) the weakest precondition of S with respect to R,since it represents the set of all states such that execution begun in any one of them will terminate with R true.
weakest表示更容易达到的条件,所以表示最大子集,一般我们只要找到符合Q=>wp(S, R)的Q。理解这点对未来的推导很重要。

Some properties of wp
Law of the Excluded Miracle: wp ( S, F) = F
Distributivity of Conjunction: wp ( S,Q ) ^ wp ( S,R ) = wp(S, Q ^ R )
Law of Monotonicity: if Q=>R then wp ( S, Q ) ^ w p ( S, R)
证明:利用引理Q=>R => Q^R=Q,则有wp(S,Q)=wp(S, Q^R)=wp(S,Q)^wp(S,R),
┓wp(S,Q)=┓(wp(S,Q)^wp(S,R))=┓wp(S,Q)V┓wp(S,R)
┓wp(S,Q)Vwp(S,R)=┓wp(S,Q)V┓wp(S,R)Vwp(S,R)=T,得证
Distributivity of Disjunction: wp(S,Q) V wp(S,R) = wp(S,QVR)
证明:原式等价于
(wp(S,Q)=>wp(S,QVR))^(wp(S,R)=>wp(S,QVR))
可以证明Q=>QVR,则上式根据7.5有左右半部分
均为T,即T^T=T,得证
wp( S, Q ) ^ wp ( S, R) = w p ( S, Q ^ R) (for deterministic S)

Chapter 8 The Commands skip,abort and Composition
skip & abort
wp(skip, R) = R
wp(abort, R) = F

(8.3) Definition. wp(“S1; S2”,R) = wp(S1, wp(S2, R)).

Chapter 9 The Assignment Command
*因为编辑器的问题,本笔记所有的

Definition: wp( "x:= e", R) = domain(e) cand Rx/e
domain(e) is a predicate that describes the set of all states in which e may be evaluated - i.e. is well-defined. It must exclude all states in which evaluation of e would be undefined.

Execution of an assignment may change only the variable indicated and evaluation of an expression may change no variable. The ban on side effects is extremely important, for it allows us to con­sider expressions as conventional mathematical entities.

The definition of wp was used to derive the assignment,and not only to show that the assignment was correct. 这句话很重要,后面第三部分的应用很多时候都是对这句话的阐释。

P137页的Remark,思路很独特,没有在本书后面发现对该例子的使用,罗列如下,目前还不是很理解
9.3 Assignment to an Array Element
本节用到之前章节提出的数组的函数表示法,对该方法的使用可以参看P140 Example 3。展开时,使用到两分法,=i的情况,和≠i的情况

Chapter 10 The Alternative Command
IF:
BB: B1 v B2 v...v Bn
① BB is well-defined
② at least one guard is true
③ Bi =>wp(Si, R)

题目: S = (10.4), R = i<=m ^ p=(N j: 0<=j<i: b[j]>0)
证明(chp10_eg2_prove.txt):
考虑下面这个式子
(b[i]>0 => i+1<=m ^ p+1=(N j:0<=j<i+1: b[j]>0)) ^ (b[i]<0 => i+1<=m ^ p=(N j:0<=j<i+1: b[j]>0))
1.因为有b[i]>0,所以上式左半边等价于(b[i]>0 => i<m ^ p=(N j:0<=j<i: b[j]>0))
2.同理上式右边等价为(b[i]<0 => i<m ^ p=(N j:0<=j<i: b[j]>0))
所以example 2的证明等价如下:
b[i]≠0 ^ (b[i]>0 => i<m ^ p=(N j:0<=j<i: b[j]>0)) ^ (b[i]<0 => i<m ^ p=(N j:0<=j<i: b[j]>0))
= b[i]≠0 ^ (b[i]≤0 V (i<m ^ p=(N j:0<=j<i: b[j]>0))) ^ (b[i]≥0 V (i<m ^ p=(N j:0<=j<i: b[j]>0)))
= b[i]≠0 ^ ((b[i]≤0 ^ b[i]≥0) V (i<m ^ p=(N j:0<=j<i: b[j]>0)))
= b[i]≠0 ^ (b[i]=0 V (i<m ^ p=(N j:0<=j<i: b[j]>0))
= b[i]≠0 ^ i<m ^ p=(N j:0<=j<i: b[j]>0)

* For purposes of efficiency the programmer could strengthen the guards to excise the nondeterminism. 程序员通常通过加强条件,来消除不确定性以提高程序效率。


Chapter 11 The Iterative Command
Choosing a true guard executing its command is called performing an iteration of the loop.

H0(R) = ┐BB ^ R
The following predicate H0(R) represents the set of states in which execution of DO terminates in 0 iterations with R true, because the guards are initially false.
Iteration: Choosing a true guard executing its command is called performing an iteration of the loop.

Hk(R) = H0(R) v wp (IF,Hk-1(R)),for k>0.

Invariant
A predicate P that is true before and after each iteration of a loop is called an invariant relation, or simply an invariant, of the loop.

Checklist for understanding a loop
1. Show that P is true before execution of the loop begins.
2. Show that {P ^ Bi}Si{P}, for 1≤i≤n. That is,execution of each guarded command terminates with P true, so that P is indeed an invariant of the loop.
3. Show that P^┐BB => R , i.e. upon termination the desired result is true.
4. Show that P^BB =>t>0,so that t is bounded from below as long as the loop has not terminated.
5. Show that {P^Bi } t1:=t; Si {t<t1}, for 1≤i≤n, so that each loop iteration is guaranteed to decrease the bound func­tion.

2,3两条很重要,一般会先得到P,再通过3得到BB,再通过2得到Si。1用来获得初始条件。4,5用来获得bound function,用来验证loop的正确性。纵观整书来说,这个check list应该就是本书的核心。

Chapter 12 Procedure Call (skip)

Part III The Development of Programs
第一部分是理论推导的基础定理,第二部分将该基础定理上升到对程序特征的推导,第三部分将此推导应用到开发程序以及验证程序的正确性。

Chapter 13 Introduction
It is just too difficult to prove an already existing program correct, and it is far better to use the proof-of-correctness ideas throughout the program­ming process for insight.

formality & common sense
What is needed is a fine balance between the two.
Principle: Use theory to provide insight; use common sense and intuition where it is suitable, but fall back on the formal theory for support when difficulties and com­plexities arise. 理解这段话很重要

Principle: Know the properties of the objects that are to be manipulated by a program.

Chapter 14 Programming as a Goal-Oriented Activity
Principle: Programming is a goal-oriented activity.
Principle: Before attempting to solve a problem, make absolutely sure you know what the problem is.
Principle: Before developing a program, make precise and refine the pre- and postconditions.
这三条要好好理解,很重要

Strategy for developing an alternative command:
①find a command C whose execution will establish postcondition R in at least some cases.
②find a Boolean B satisfying B=>wp(C, R)
③repeat ①② ---> B1vB2v...vBn = T

关于推导的两个例子如下:eg1 (P186) & eg2 (P189)

1.启动流程
     有两根boot pin,来选择启动设备,一般是从flash启动,当然也可以从system memory或者SRAM启动。
     如果走system memory启动的话,其实是走ST固化在这片存储区的Embedded boot loader。这其实就是一个bootloader,他能提供给我们很多其他的功能,比如IAP(In-Application Programming)
再看memory map,以STM32F103ZET6为例,主要是看block 0

0x0000 0000 ~ 0x0007 FFFF这一段,会被boot pin连接到flash(sram,system memory)上
系统上电后,系统会跑reset流程,当进入软件流程时,如下进行
在离开复位状态后,CM3做的第一件事就是读取下列两个 32位整数的值:
  •      从地址 0x0000,0000处取出 MSP的初始值。 
  •      从地址 0x0000,0004 处取出 PC 的初始值——这个值是复位向量,LSB 必须是 1。然后从这
个值所对应的地址处取指。


系统reset向量一定是要在0地址处,而异常向量表可以通过SCB_VTOR来修改。而且向量表的结构是固定的,详情请见下图
再看DMR的中断向量代码:
__root  const  APP_INTVECT_ELEM  __vector_table[] @ ".intvec" = {
    { .Ptr = (void *)__sfe( "CSTACK" )},  /*  0, SP start value. */
    __iar_program_start,                  /*  1, PC start value. */
    App_NMI_ISR,                          /*  2, NMI.            */
    App_Fault_ISR,                        /*  3, Hard Fault.     */
        ……
}

APP_INTVECT_ELEM  是一个union
typedef  union {
    CPU_FNCT_VOID   Fnct;
    void           *Ptr;
} APP_INTVECT_ELEM;

  • 第一个成员就是MSP(Main Stack Pointer)值,__sfe( "CSTACK" )指CSTACK段的下一个字节,因为Coretex M3的堆栈是向上生长的,所以这个值正好是栈底的位置。
  • 第二个成员__iar_program_start则是IAR提供的一个标准函数,其源代码并不可见,但他会调用几个函数来完成全局变量以及外部memory的初始化。其内容可参考[原创]在main()之前,IAR都做了啥? - jk的专栏 - 博客频道 - CSDN.NET