<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom"><title type="text">博客园_cat todd.log | grep 'programming' | sort -r</title><subtitle type="text"/><id>http://feed.cnblogs.com/blog/u/36931/rss</id><updated>2012-01-28T15:09:47Z</updated><author><name>Todd Wei</name><uri>http://www.cnblogs.com/weidagang2046/</uri></author><generator>CNBlogs BlogServer</generator><link rel="alternate" type="text/html" href="http://www.cnblogs.com/weidagang2046/"/><link rel="self" type="application/atom+xml" href="http://feed.cnblogs.com/blog/u/36931/rss"/><entry><id>http://www.cnblogs.com/weidagang2046/archive/2011/10/30/fluent-interface.html</id><title type="text">API设计新思维：用流畅接口构造内部DSL</title><summary type="text">普通的API抽象方式存在天然的缺陷，通过基本元素/语义构造更高级抽象元素/语义的时候，语言的构造规则很大程度上限制了抽象的维度，我们很难跳出这个维度去，甚至可能根本意识不到这个限制。流畅接口不同于传统的API设计，它突破了语言抽象机制带来的定势思维，根据问题域选取适当的抽象维度，利用语言的基本语法构造领域特定的语义和语法。本文介绍了流畅接口的构造的4种典型抽象：基本语义抽象，管道抽象，层次结构抽象，异步抽象。</summary><published>2011-10-30T13:15:00Z</published><updated>2011-10-30T13:15:00Z</updated><author><name>Todd Wei</name><uri>http://www.cnblogs.com/weidagang2046/</uri></author><link rel="alternate" href="http://www.cnblogs.com/weidagang2046/archive/2011/10/30/fluent-interface.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/weidagang2046/archive/2011/10/30/fluent-interface.html"/><content type="html">&lt;p&gt;程序设计语言的抽象机制包含了两个最基本的方面：一是语言关注的基本元素/语义；另一个是从基本元素/语义到复合元素/语义的构造规则。在C、C++、Java、C#、Python等通用语言中，语言的基本元素/语义往往离问题域较远，通过API库的形式进行层层抽象是降低问题难度最常用的方法。比如，在C语言中最常见的方式是提供函数库来封装复杂逻辑，方便外部调用。&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;不过普通的API设计方法存在一种天然的陷阱，那就是不管怎样封装，大过程虽然比小过程抽象层次更高，但本质上还是过程，受到过程语义的制约。也就是说，通过基本元素/语义构造更高级抽象元素/语义的时候，语言的构造规则很大程度上限制了抽象的维度，我们很难跳出这个维度去，甚至可能根本意识不到这个限制。而SQL、HTML、CSS、make等DSL（领域特定语言）的抽象维度是为特定领域量身定做的，从这些抽象角度看问题往往最为简单，所以DSL在解决其特定领域的问题时比通用程序设计语言更加方便。通常，SQL等非通用语言被称为外部DSL（External DSL）；在通用语言中，我们其实也可以在一定程度上突破语言构造规则的抽象维度限制，定义内部DSL（Internal DSL）。&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;本文将介绍一种被称为流畅接口（Fluent Interface）的内部DSL设计方法。Wikipedia上&lt;a title="Fluent Interface" href="http://en.wikipedia.org/wiki/Fluent_interface"&gt;Fluent Interface&lt;/a&gt;的定义是：&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;div&gt;   &lt;div&gt;A fluent interface (as first coined by Eric Evans and Martin Fowler) is an implementation of an object oriented API that aims to provide for more readable code. A fluent interface is normally implemented by using method chaining to relay the instruction context of a subsequent call (but a fluent interface entails more than just method chaining).&lt;/div&gt;    &lt;p&gt;&lt;/p&gt;    &lt;p&gt;下面将分4个部分来逐步说明流畅接口在构造内部DSL中的典型应用。 &lt;/p&gt; &lt;/div&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;1. 基本语义抽象&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;如果要输出0..4这5个数，我们一般会首先想到类似这样的代码：&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040"&gt;//Java&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040"&gt;for (int i = 0; i &amp;lt; 5; ++i) {&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; system.out.println(i);&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040"&gt;}&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;而Ruby虽然也支持类似的for循环，但最简单的是下面这样的实现：&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040"&gt;//Ruby&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040"&gt;5.times { |i| puts i }&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;Ruby中一切皆对象，5是Fixnum类的实例，times是Fixnum的一个方法，它接受一个block参数。相比for循环实现，Ruby的times方式更简洁，可读性更强，但熟悉OOP的朋友可能会有疑问，times是否应该作为整型类的方法呢？在OOP中，方法调用通常代表了向对象发送消息，改变或查询对象的状态，times方法显然不是对整型对象状态的查询和修改。如果你是Ruby的设计者，你会把times方法放入Fixnum类吗？如果答案是否定的，那么Ruby的这种设计本质上代表了什么呢？实际上，这里的times虽然只是一个普通的类方法，但它的目的却与普通意义上的类方法不同，它的语义实际上类似于for循环这样的语言基本语义，可以被视为一种自定义的基本语义。times的语义从一定程度上跳出了类方法的框框，向问题域迈进了一步！&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;另一个例子来自Eric Evans的&amp;#8220;用两个时间点构造一个时间段对象&amp;#8221;，普通设计：&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040"&gt;//Java&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040"&gt;TimePoint fiveOClock, sixOClock;      &lt;br /&gt;TimeInterval meetingTime = new TimeInterval(fiveOClock, sixOClock);&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;Evans的设计是这样的：&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040"&gt;//Java&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040"&gt;TimeInterval meetingTime = fiveOClock.until(sixOClock);&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;按传统OO设计，until方法本不应出现在TimePoint类中，这里TimePoint类的until方法同样代表了一种自定义的基本语义，使得表达时间域的问题更加自然。&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;虽然上面的两个简单例子和普通设计相比看不出太大的优势，但它却为我们理解流畅接口打下了基础。重要的是应该体会到它们从一定程度上跳出了语言基本抽象机制的束缚，我们不应该再用类职责划分、迪米特法则（Law of Demeter）等OO设计原则来看待它们。&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;2. 管道抽象&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;在Shell中，我们可以通过管道将一系列的小命令组合在一起实现复杂的功能。管道中流动的是单一类型的文本流，计算过程就是从输入流到输出流的变换过程，每个命令是对文本流的一次变换作用，通过管道将作用叠加起来。在Shell中，很多时候我们只需要一句话就能完成log统计这样的中小规模问题。和其他抽象机制相比，管道的优美在于无嵌套。比如下面这段C程序，由于嵌套层次较深，不容易一下子理解清楚：&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040"&gt;//C&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040"&gt;min(max(min(max(a, b), c), d), e)&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;br /&gt;&lt;/p&gt;  &lt;p&gt;而用管道来表达同样的功能则清晰得多： &lt;/p&gt;  &lt;p&gt;&lt;font color="#800040"&gt;//Bash&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040"&gt;max $a $b | min $c | max $d | min $e&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;我们很容易理解这段程序表达的意思是：先求a, b的最大值；再把结果和c取最小值；再把结果和d求最大值；再把结果和e求最小值。&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;jQuery的链式调用设计也具有管道的风格，方法链上流动的是同一类型的jQuery对象，每一步方法调用是对对象的一次作用，整个方法链将各个方法的作用叠加起来。&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040"&gt;//jQuery&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040"&gt;$('li').filter(':even').css('background-color', 'red');&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;3. 层次结构抽象&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;除了管道这种&amp;#8220;线性&amp;#8221;结构外，流畅接口还可用于构造层次结构抽象。比如，用Javascript动态创建创建下面的HTML片段：&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040"&gt;&amp;lt;div id=&amp;#8217;product_123&amp;#8217; class=&amp;#8217;product&amp;#8217;&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040"&gt;&amp;nbsp;&amp;nbsp; &amp;lt;img src=&amp;#8217;preview_123.jpg&amp;#8217; /&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;ul&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;li&amp;gt;Name: iPad2 32G&amp;lt;/li&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;li&amp;gt;Price: 3600&amp;lt;/li&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/ul&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040"&gt;&amp;lt;/div&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;若采用Javascript的DOM API，其可读性和可维护性都不够好：&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040"&gt;//Javascript&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040"&gt;var div = document.createElement('div');&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040"&gt;div.setAttribute(&amp;#8216;id&amp;#8217;, &amp;#8216;product_123&amp;#8217;);&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040"&gt;div.setAttribute(&amp;#8216;class&amp;#8217;, &amp;#8216;product&amp;#8217;);&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040"&gt;var img = document.createElement('img');&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040"&gt;img.setAttribute(&amp;#8216;src&amp;#8217;, &amp;#8216;preview_123.jpg&amp;#8217;);&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040"&gt;div.appendChild(img);&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040"&gt;var ul = document.createElement('ul');&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040"&gt;var li1 = document.createElement('li');&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040"&gt;var txt1 = document.createTextNode("Name: iPad2 32G");&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040"&gt;li1.appendChild(txt1);&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040"&gt;&amp;#8230;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040"&gt;div.appendChild(ul);&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;而下面流畅接口API则要有表现力得多：&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040"&gt;//Javascript&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040"&gt;var obj = &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040"&gt;$.div({id:&amp;#8217;product_123&amp;#8217;, class:&amp;#8217;product&amp;#8217;})&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; .img({src:&amp;#8217;preview_123.jpg&amp;#8217;})&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; .ul()&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; .li().text(&amp;#8216;Name: iPad2 32G&amp;#8217;)._li()&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; .li().text(&amp;#8216;Price: 3600&amp;#8217;)._li()&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; ._ul()&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040"&gt;._div()&lt;/font&gt;;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;和Javascript的标准DOM API相比，上面的API设计不再局限于孤立地看待某一个方法，而是考虑了它们在解决问题时的组合使用，所以代码特别贴近问题的本质。这样的代码是自解释的（self-explanatory）在可读性方面要明显胜于DOM API，这相当于定义了一种类似于HTML的内部DSL，它拥有自己的语义和语法。需要特别注意的是，上面的层次结构抽象和管道抽象有着本质的不同，管道抽象的方法链上通常是同一对象的连续传递，而层次抽象中方法链上的对象却在随着层次的变化而变化。此外，我们还可以把业务规则也表达在流畅接口中，比如上面的例子中，body()不能包含在div()返回的对象中，div().body()将抛出"body方法不存在&amp;#8221;异常。&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;4. 异步抽象&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;div&gt;流畅接口不仅可以构造复杂的层次抽象，还可以用于构造异步抽象。在基于回调机制的异步模式中，多个异步调用的同步和嵌套问题是使用异步的难点所在。有时一个稍复杂的调用和同步关系会导致代码充满了复杂的同步检查和层层回调，难以理解和维护。这个问题从本质上讲和上面HTML的例子一样，是由于多数通用语言并未把异步作为基本元素/语义，许多异步实现模式是向语言的妥协。针对这个问题，我用Javascript编写了一个基于流畅接口的异步DSL，示例代码如下：&lt;/div&gt;  &lt;p&gt;&lt;font color="#800040"&gt;//Javascript&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040"&gt;$.begin()      &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; .async(newTask('task1'), 'task1')       &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; .async(newTask('task2'), 'task2')       &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; .async(newTask('task3'), 'task3')       &lt;br /&gt;.when()       &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; .each_done(&lt;/font&gt;&lt;font color="#800040"&gt;function(name, result) { &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; console.log(name + ': ' + result);})      &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; .all_done(function(){ console.log('good, all completed'); })       &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; .timeout(       &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; function(){&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; console.log('timeout!!');       &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; $.begin()       &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; .async(newTask('task4'), 'task4')       &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; .when()       &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; .each_done(function(name, result) { &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; console.log(name + ': ' + result); })      &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; .end();&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }       &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; , 3000)&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040"&gt;.end();&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;div&gt;上面的代码只是一句Javascript语句，但从另一个角度看它却像一段描述异步调用的DSL程序。它通过流畅接口定义了begin when end的语法结构，begin后面跟的是启动异步调用的代码；when后面是异步结果处理，可以选择each_done, all_done, timeout中的一种或多种。而begin when end结构本身是可以嵌套的，比如上面的代码在timeout处理分支中就包含了另一个begin when end结构。通过这个DSL，我们可以比基于回调的方式更好地表达异步调用的同步和嵌套关系。&lt;/div&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;上面介绍了用流畅接口构造的4种典型抽象，除此之外还有很多其他的抽象和应用场合，比如：不少单元测试框架就通过流畅接口定义了单元测试的DSL。虽然上面的例子以Javascript等动态语言居多，但其实流畅接口所依赖的语法基础并不苛刻，即使在Java这样的静态语言中，同样可以轻松地使用。流畅接口不同于传统的API设计，理解和使用流畅接口关键是要突破语言抽象机制带来的定势思维，根据问题域选取适当的抽象维度，利用语言的基本语法构造领域特定的语义和语法。&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;参考&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;a title="Wikipedia: Fluent Interface" href="http://en.wikipedia.org/wiki/Fluent_interface"&gt;Wikipedia: Fluent Interface&lt;/a&gt;&amp;nbsp;&lt;/p&gt;  &lt;p&gt;&lt;a title="Martin Fowler: Fluent Interface" href="http://www.martinfowler.com/bliki/FluentInterface.html"&gt;Martin Fowler: Fluent Interface&lt;/a&gt;     &lt;br /&gt;&lt;/p&gt;  &lt;p&gt;&lt;a title="jQuery is DSL" href="http://www.cnblogs.com/cathsfz/archive/2009/08/10/1543266.html"&gt;jQuery is DSL&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;a title="An Approach to Internal Domain-Specific Languages in Java" href="http://www.infoq.com/articles/internal-dsls-java"&gt;An Approach to Internal Domain-Specific Languages in Java&lt;/a&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/weidagang2046/aggbug/2229293.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/weidagang2046/archive/2011/10/30/fluent-interface.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/weidagang2046/archive/2011/10/15/2213672.html</id><title type="text">“品质在于构建过程”吗？</title><summary type="text">模型是软件的灵魂，在软件诞生之前就已经存在于设计者的心中，而软件的构建过程正是心中的世界向现实世界逐渐投影。如同光具有波粒二象性，软件开发也具有艺术创作和工业生产的二象性，它包含了柔性的设计和刚性的过程。</summary><published>2011-10-15T13:30:00Z</published><updated>2011-10-15T13:30:00Z</updated><author><name>Todd Wei</name><uri>http://www.cnblogs.com/weidagang2046/</uri></author><link rel="alternate" href="http://www.cnblogs.com/weidagang2046/archive/2011/10/15/2213672.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/weidagang2046/archive/2011/10/15/2213672.html"/><content type="html">&lt;p&gt;今天在微博上看到几位敏捷爱好者探讨敏捷测试和质量保证问题，我忍不住也加入了讨论：&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040"&gt;&lt;strong&gt;Z先生原帖：&lt;/strong&gt;我刚才看到一个大会演讲稿，谈到敏捷测试六大指导原则：1.仅靠测试人员不可能获得高质量的软件，质量是整个研发团队的责任；2. 场景是不可穷举的，测试活动必须是风险驱动的，关注于高风险的场景；3.分层自动化测试是唯一出路;4.在正确的位置进行恰当的测试是自动化的关键；【待续】&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040"&gt;&lt;strong&gt;S先生回复：&lt;/strong&gt;品质在于构建过程。检验贯穿构建过程，提供及时反馈。&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040"&gt;&lt;strong&gt;我回复：&lt;/strong&gt;什么样的构建过程才能出Unix这样的品质呢？迭代？快速反馈？TDD?&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040"&gt;&lt;strong&gt;S先生回复：&lt;/strong&gt;据说stroustrup听到重构时的反应是，我们从七十年代就这样做了。推荐《UNIX编程环境》，了解大师的编程方式。&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040"&gt;&lt;strong&gt;我回复：&lt;/strong&gt;您偷换了概念。不能说大师用了重构，C++和UNIX的品质就是靠重构或某种构建过程得来的。厨师做菜用到了勺子，不等于菜好吃是因为勺子。&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040"&gt;&lt;strong&gt;S先生回复：&lt;/strong&gt;我没有概念。我们看到一个果，就问因是什么。其实是泛因果，无因果，一切是机缘凑巧。&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040"&gt;&lt;strong&gt;我回复：&lt;/strong&gt;&amp;#8220;品质在于构建过程&amp;#8221;难道不是一个明白的因果描述吗？&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040"&gt;&lt;strong&gt;S先生回复：&lt;/strong&gt;品质在于构建的人。我说话时没因果，你看到了因果。&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040"&gt;&lt;strong&gt;我回复：&lt;/strong&gt;欢迎敏捷爱好者围观！&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;很高兴几个回合讨论下来S先生修正了先前&amp;#8220;品质在于构建过程&amp;#8221;的观点。什么重构、TDD、迭代、快速反馈等等构建过程都不是Unix品质的核心要素。我不但不认同&amp;#8220;品质在于构建过程&amp;#8221;、&amp;#8220;测试是最好的设计方法&amp;#8221;这类机械式的观点，而且也不满意把软件优劣归结于&amp;#8220;人是根本&amp;#8221;的简单回答。我们需要探索一个既非机械式的，也非简单地归结为某种理念的更深刻的答案。&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;像Unix这样优秀的软件，真正的核心要素到底是什么呢？我的答案是：模型，即人心中的软件。在看得见、摸得着之前，Unix的品质就已经存在于设计者的心中了，他们不会在Unix诞生后惊讶：&amp;#8220;哇，Unix的稳定性这么好，7x24小时运行，从来不蓝屏&amp;#8221;。模型一定是设计者心中最美的东西，为什么我们阅读操作系统源代码会像进入迷宫一般理不清头绪，而作者自己却觉得头头是道呢？因为作者早已&amp;#8220;胸有成竹&amp;#8221;，我们以为他几十万行代码敲很辛苦，实际上在他自己看来是按部就班一步步向目标靠近。 &lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;模型是软件的灵魂，存在于设计者的心中，而软件的构建过程正是心中的世界向现实世界逐渐投影。模型可以是完美的，而现实却非完美，或许有时候我们很幸运地到达了，或许有时候我们不得不向现实妥协，改变心中的世界。试图制造灯泡的爱迪生可能会一时找不到熔点极高的发光金属而止步不前，企图制造永动机的人则根本无法实现。在不完美的现实中，我们明明想的是a+b，却敲成了a-b；我们以为某个API可以很快返回，没想到却等了5秒钟，为了不阻塞用户不得不改成了异步。Review、测试等构建过程在一定程度上弥补了现实的不完美，并对模型给予了反馈，但它却无法决定软件的特质。如果设计者心中没有Unix，即使每个实现环节都层层检验，拥有光速般的反馈，他有怎么能构建出Unix呢？Windows NT内核和Windows 3.1内核的品质差别不在于微软采用了两种不同的构建过程，而在于它们采用了不同的内核模型。灵魂与躯体的差别就在于此！虽然对于普通的软件开发通常有不少成熟的模型供选择，并不需要总是创造自己的模型，但理解模型间的差异，并在设计时选用恰当的模型仍然比采用某种构建过程更加重要。服务器架构采用Nginx似的异步IO模型，还是采用Apache似的每个请求一个线程的模型远比开发是否采用了TDD更为重要。&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;模型的产生是柔性的，主要源于灵感；过程的执行是刚性的，主要源于逻辑。苹果砸在牛顿的脑袋上能砸出万有引力模型，砸在我们脑袋上却只是&amp;#8220;哎呦&amp;#8221;一声；但一个苹果3元钱，两个苹果2*3=6元钱却在牛顿和我们面前是平等的。迷信灵感和迷信逻辑是两个错误的极端，孔子讲&amp;#8220;天下国家可均也，爵禄可辞也，白刃可蹈也，中庸不可能也&amp;#8221;，任何一项技能的高级阶段都是关于&amp;#8220;度&amp;#8221;的艺术。如同光具有波粒二象性，软件开发也具有艺术创作和工业生产的二象性，它包含了柔性的设计和刚性的过程。越是不成熟的前沿领域越表现出柔性特征；越是成熟的一般领域越表现出工业生产的特征。因此，一个以新产品为主的创业型公司应当更注重设计，更需要画家、诗人般的创造型人才；而业务成熟产品稳定的大公司应当更注重过程，更需要踏踏实实的生产线工人似的人才。但在当今这个瞬息万变的信息时代，即使是世界500强的大公司也越来越不稳定，越来越需要创新才能适应，所以即使大公司也不可忽视软件开发的柔性特征。同时，我们也不能迷信模型，过程同样可以成为企业的核心竞争力，比如：富士康。虚虚实实，实实虚虚，其妙无穷。老外做Nike品牌（虚），我们做代工生产（实），高额利润被老外拿走了；我们经营航空公司（虚），老外生产波音飞机（实）高价卖给我们，高额利润又被老外拿走了。靠虚取胜还是靠实取胜？这是个问题^_^&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;或许我对于模型柔性的描述不太让人满意，人们多习惯于有章可循的感觉，即便不是死板的知识，起码要找个&amp;#8220;在某某思想的指导下&amp;#8221;才觉得心里有着落。或许还有人说，模型的确重要，那么我们能不能有一个过程、模式或套路来推导出模型呢？比如，现在非常流行的从用户需求出发的分析模式，即&amp;#8220;分析需求，抽象出共性，共性是本质的，本质是稳定的&amp;#8221;，这类模式的特点符合人们希望找到套路的心理，一看就明白，容易操作，有成就感。我不否认这类模式的确可以得出可用的软件设计，沿用成熟的模型也未尝不可。但我们应该明白，心中的世界远比现实的世界更广大更美妙。世界是多元的，用户需求、成熟模型等直接可见的东西只代表了某几个维度的视图，设计者心中应当有更多的维度！用户需要一个文本编辑器，是设计者心中的世界决定了他交出的作品是Vi，还是Emacs，亦或是Notepad。亨利&amp;#183;福特说：&amp;#8220;如果你问用户需要什么，他会告诉你一匹更快的马&amp;#8221;。汽车源于福特心中的世界，这是一个比只有马的世界更多彩的世界。乔布斯是一个不重视市场调研的人，iPod，iPhone，iPad都不是发个问卷，做个市场调查看看用户需要什么的结果。Apple是乔布斯心中的世界在现实中的投影！所以，请打破&amp;#8220;从用户需求出发&amp;#8221;，&amp;#8220;从模式出发&amp;#8221;的迷信，释放你的想象力，让自己心中的世界去包容现实的世界吧！&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;每个人心中都有一个属于自己的世界，牛顿运动定律是牛顿心中的世界，相对论是爱因斯坦心中的世界。哪一个才是本来的世界呢？有没有本来的世界呢？本来的世界是什么样子呢？&amp;#8230; 老子给我们启示&amp;#8220;道可道，非常道&amp;#8221;，说得清，道得明，想得到的都不是永恒的真理，所以真理不可言说，对真理的探索永远没有止境&amp;#8230;&amp;#8230;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/weidagang2046/aggbug/2213672.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/weidagang2046/archive/2011/10/15/2213672.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/weidagang2046/archive/2011/08/14/object-messaging-model.html</id><title type="text">对象的消息模型</title><summary type="text">本文主要介绍了对象的消息模型的特征，并比较了C++对象模型，Java、C#等基于类的静态语言中的对象模型与严格消息模型的差异，最后探讨了Method Missing相关话题。</summary><published>2011-08-14T09:28:00Z</published><updated>2011-08-14T09:28:00Z</updated><author><name>Todd Wei</name><uri>http://www.cnblogs.com/weidagang2046/</uri></author><link rel="alternate" href="http://www.cnblogs.com/weidagang2046/archive/2011/08/14/object-messaging-model.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/weidagang2046/archive/2011/08/14/object-messaging-model.html"/><content type="html">&lt;p&gt;&lt;strong&gt;&lt;font face="Lucida Console"&gt;C++对象模型&lt;/font&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;font face="Lucida Console"&gt;&lt;/font&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;span style="font-family: 'Lucida Console'" &gt;话题从下面这段C++程序说起，你认为它可以顺利执行吗？&lt;/span&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040" face="Lucida Console"&gt;//C++&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040" face="Lucida Console"&gt;class A {      &lt;br /&gt;public:       &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; void Hello(const std::string&amp;amp; name) {       &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; std::cout &amp;lt;&amp;lt; "hello " &amp;lt;&amp;lt; name;       &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }       &lt;br /&gt;}; &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040" face="Lucida Console"&gt;int main(int argc, char** argv) {      &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; A* pa = NULL;&amp;nbsp; //!!       &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; pa-&amp;gt;Hello("world");       &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; return 0;       &lt;br /&gt;}&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;试试的确可以顺利运行输出hello world，奇怪吗？其实并不奇怪，根据C++对象模型，类的非虚方法并不会存在于对象内存布局中，实际上编译器是把Hello方法转化成了类似这样的全局函数：&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040" face="Lucida Console"&gt;void A_Hello_xxx(A * const this, const std::string&amp;amp; name) { &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040" face="Lucida Console"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; std::cout &amp;lt;&amp;lt; &amp;#8220;hello &amp;#8220; &amp;lt;&amp;lt; name; &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040" face="Lucida Console"&gt;}&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;对象指针其实是作为第一个参数被隐式传递的，pa-&amp;gt;Hello(&amp;#8220;world&amp;#8221;)实际上是调用的A_Hello_xxx(pa, &amp;#8220;world&amp;#8221;)，而恰好A_Hello_xxx内部没有使用pa，所以这段代码得以顺利运行。&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;&lt;/font&gt;&lt;/p&gt;  &lt;br /&gt;  &lt;p&gt;&lt;strong&gt;&lt;font face="Lucida Console"&gt;对象的消息模型&lt;/font&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;font face="Lucida Console"&gt;&lt;/font&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;如果是研究C++对象模型，上面的讨论可以到此为止，不过这里我想从另一个角度来继续探讨这个问题。OOP的先驱人物Alan Kay在总结Smalltalk的OO特征时强调：&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;Smalltalk is not only NOT its syntax or the class library, it is not even about classes. I'm sorry that I long ago coined the term "objects" for this topic because it gets many people to focus on the lesser idea. The big idea is "messaging".&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;也就是说相比类和对象的概念来讲，他认为对象交互的消息模型是OOP更为本质的特征，因为消息关注的是对象间的接口和交互，在构建大的系统的时候重要的不是对象的内部状态，而是它们的交互。根据消息模型，牛.吃(草) 的语义是发送一条消息给&amp;#8220;牛&amp;#8221;，消息的类型是&amp;#8220;吃&amp;#8221;，消息的内容是&amp;#8220;草&amp;#8221;。如果按照严格的消息模型，那么上面那段C++代码应解释为向一个NULL对象发送Hello消息，这显然是不应该顺利执行的。类似的代码如果是在Java或C#中则会抛出空引用异常，所以Java和C#的设计更符合消息模型。&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;不过，Java和C#中也并非完全符合消息模型，来看一个经典的封装问题：&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040" face="Lucida Console"&gt;//C#&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040" face="Lucida Console"&gt;public class Account {      &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; private int _amount;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040" face="Lucida Console"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public void Transfer(Account acc, int delta) {      &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; acc._amount += delta;       &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; this._amount -= delta;       &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040" face="Lucida Console"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;#8230;      &lt;br /&gt;}&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;上面定义了一个Account类，问题在于为什么在这个类的Transfer方法中可以直接访问另一个对象acc的私有成员_amount呢？这是不是有破坏封装的嫌疑呢？这个问题经典的答案是：并不破坏封装，封装是划分了基于类的静态的代码边界，使得类的private代码修改不影响外界，而不是对于动态对象的保护。这个解释当然是合理的，不过正如上面C++代码的解释属于C++对象模型范畴，这个解释则属于基于类的静态类型OOP语言的范畴。消息模型强调了对象内部状态的保护，只能通过消息改变其状态，而对象内部是否真的具有_amout这样一个私有成员对其他任何对象（即使同类对象）都是未知的。&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;如果要严格遵守消息模型实现对象内部状态的保护应该怎么做呢？我们来看一个例子，定义一个集合类，包括：1.集合对象的构造函数；2.In方法：判断元素是否存在；3.Join方法：对两个集合做交集；4.Union方法：对两个集合做并集。下面是一种Javascript实现：&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040" face="Lucida Console"&gt;//Javascript&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040" face="Lucida Console"&gt;//集合类Set的构造函数&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040" face="Lucida Console"&gt;function Set() {      &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; var _elements = arguments;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040" face="Lucida Console"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; //In方法：判断元素e是否在集合中      &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; this.In = function(e) {       &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; for (var i = 0; i &amp;lt; _elements.length; ++i) {       &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (_elements[i] == e) return true;       &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }       &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return false;       &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; };       &lt;br /&gt;} &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040" face="Lucida Console"&gt;//Join方法：对两个集合求交集&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040" face="Lucida Console"&gt;Set.prototype.Join = function(s2) {      &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; var s1 = this;       &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; var s = new Set();       &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; s.In = function(e) { return s1.In(e) &amp;amp;&amp;amp; s2.In(e); }       &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; return s;       &lt;br /&gt;}; &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040" face="Lucida Console"&gt;//Union方法：对两个集合求并集&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040" face="Lucida Console"&gt;Set.prototype.Union = function(s2) {      &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; var s1 = this;       &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; var s = new Set();       &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; s.In = function(e) { return s1.In(e) || s2.In(e); }       &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; return s;       &lt;br /&gt;}; &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040" face="Lucida Console"&gt;var s1 = new Set(1, 2, 3, 4, 5);      &lt;br /&gt;var s2 = new Set(2, 3, 4, 5, 6);       &lt;br /&gt;var s3 = new Set(3, 4, 5, 6, 7);       &lt;br /&gt;assert(false == s1.Join(s2).Join(s3).In(2));       &lt;br /&gt;assert(true == s1.Join(s2).Uion(s3).In(7));&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;如果是在静态类型OOP语言中，要实现集合类的Join或Union，我们多半会像上面Account的例子一样直接对s2内部的_elements进行操作，而上面这段Javascript定义的Set关于对象s2的访问完全是符合消息模型的基于接口的访问。要实现消息模型Javascript的prototype机制并非必须的，真正的关键在于函数式的高阶函数和闭包特性。从这个例子我们也可以体会到函数式的优点不仅在于无副作用，函数的可组合性也是函数式编程强大的原因。&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;br /&gt;&lt;strong&gt;&lt;font face="Lucida Console"&gt;Method Missing&lt;/font&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;font face="Lucida Console"&gt;&lt;/font&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;接下来我们还要进行深度历险，让我们思考一下如果发送一条对象不能识别的消息会怎样？这种情况在C++、Java、C#等静态语言中会得到一个方法未定义的编译错误，如果是在Javascript中则会产生运行时异常。比如，&lt;/font&gt;s1.count()&lt;span style="font-family: 'Lucida Console'" &gt;会产生一个运行时异常：Object #&amp;lt;Set&amp;gt; has no method 'count'。&lt;/span&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;在静态语言这个问题很少受到重视，但在动态语言中却大有文章，来看下面的例子：&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040" face="Lucida Console"&gt;//Ruby&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040" face="Lucida Console"&gt;builder = Builder::XmlMarkup.new      &lt;br /&gt;xml = builder.books {|b|       &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; b.book :isbn =&amp;gt; "14134" do       &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; b.title "Revelation Space"       &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; b.author "Alastair Reynolds"       &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; end &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040" face="Lucida Console"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; b.book :isbn =&amp;gt; "53534" do      &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; b.title "Accelerando"       &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; b.author "Charles Stross"       &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; end       &lt;br /&gt;}&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;上面这段很DSL的Ruby代码创建了这样一个XML文件对象：&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040" face="Lucida Console"&gt;&amp;lt;books&amp;gt;      &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;book isbn="14134"&amp;gt;       &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;title&amp;gt;Revelation Space&amp;lt;/title&amp;gt;       &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;author&amp;gt;Alastair Reynolds&amp;lt;/author&amp;gt;       &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/book&amp;gt;       &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;book isbn="53534"&amp;gt;       &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;title&amp;gt;Accelerando&amp;lt;/title&amp;gt;       &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;author&amp;gt;Charles Stross&amp;lt;/author&amp;gt;       &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/book&amp;gt;       &lt;br /&gt;&amp;lt;/books&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;builder.books, b.book, b.title都是对象方法调用，由于XML的元素名是任意的，所以不可能事先定义这些方法，类似的代码如果是在Javascript中就是no method异常。那为什么上面的Ruby代码可以正确执行呢？其实只要理解了消息模型就很容易想明白，只需要定义一个通用的消息处理方法，所有未明确定义的消息都交给它来处理就行了，这就是所谓的Method Missing模式：&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040" face="Lucida Console"&gt;class Foo      &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; def method_missing(method, *args, &amp;amp;block)&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040" face="Lucida Console"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;#8230;      &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; end       &lt;br /&gt;end&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;Method Missing除了对实现DSL很重要外，还可用于产生更好地调试和错误信息，把参数嵌入到方法名中等场合。目前，Ruby、Python、Groovy几种语言对Method Missing都有很好的支持，甚至在C# 4.0中也可以利用动态特性实现。&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;&amp;nbsp;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;font face="Lucida Console"&gt;总结&lt;/font&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;font face="Lucida Console"&gt;&lt;/font&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;本文主要介绍了对象的消息模型的特征，并比较了C++对象模型，Java、C#等基于类的静态语言中的对象模型与严格消息模型的差异，最后探讨了Method Missing相关话题。&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;&amp;nbsp;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;font face="Lucida Console"&gt;参考&lt;/font&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://book.douban.com/subject/1484262/"&gt;&lt;font face="Lucida Console"&gt;Inside the C++ Object Model&lt;/font&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://book.douban.com/subject/4031906/"&gt;&lt;font face="Lucida Console"&gt;冒号课堂 - 编程范式与OOP思想&lt;/font&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://c2.com/cgi/wiki?AlanKaysDefinitionOfObjectOriented"&gt;&lt;font face="Lucida Console"&gt;Alan Kays Definition Of Object Oriented&lt;/font&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://fitzgeraldnick.com/weblog/39/"&gt;&lt;font face="Lucida Console"&gt;OOP The Good Parts: Message Passing, Duck Typing, Object Composition, and not Inheritance&lt;/font&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://olabini.com/blog/2010/04/patterns-of-method-missing/"&gt;&lt;font face="Lucida Console"&gt;Patterns of Method Missing&lt;/font&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://haacked.com/archive/2009/08/26/method-missing-csharp-4.aspx"&gt;&lt;font face="Lucida Console"&gt;Fun With Method Missing and C# 4&lt;/font&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;&lt;/font&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/weidagang2046/aggbug/2138059.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/weidagang2046/archive/2011/08/14/object-messaging-model.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/weidagang2046/archive/2011/06/27/data-affinity.html</id><title type="text">语言的数据亲和力</title><summary type="text">目前，程序设计语言似乎进入了一个蓬勃发展的时期，Javascript、Perl、Python、Ruby、Groovy等一批较新的语言正越来越多地被熟悉和使用，而C++、C#、Java等主流语言也在不断地融入函数式和动态性特征。程序员的百宝箱中可供选择的宝贝是越来多了，而社区中关于语言间的比较和争论也更为热烈，我们常常见到关于“面向过程和面向对象的比较”、“动态语言和静态语言的比较”、“命令式和函数...</summary><published>2011-06-27T15:52:00Z</published><updated>2011-06-27T15:52:00Z</updated><author><name>Todd Wei</name><uri>http://www.cnblogs.com/weidagang2046/</uri></author><link rel="alternate" href="http://www.cnblogs.com/weidagang2046/archive/2011/06/27/data-affinity.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/weidagang2046/archive/2011/06/27/data-affinity.html"/><content type="html">&lt;p&gt;&lt;font face="Lucida Console"&gt;目前，程序设计语言似乎进入了一个蓬勃发展的时期，Javascript、Perl、Python、Ruby、Groovy等一批较新的语言正越来越多地被熟悉和使用，而C++、C#、Java等主流语言也在不断地融入函数式和动态性特征。程序员的百宝箱中可供选择的宝贝是越来多了，而社区中关于语言间的比较和争论也更为热烈，我们常常见到关于&amp;#8220;面向过程和面向对象的比较&amp;#8221;、&amp;#8220;动态语言和静态语言的比较&amp;#8221;、&amp;#8220;命令式和函数式范式的比较&amp;#8221;等比较。我注意到这类讨论的关注点多集中于设计相关话题，如&amp;#8220;动态语言的Duck typing多态和静态语言的继承多态的比较&amp;#8221;，&amp;#8220;Prototype based和Class based的比较&amp;#8221;等。但我认为还有一个十分重要的方面值得关注，这就是数据处理。&lt;/font&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;数据处理之所以重要是因为不论是本地信息存储还是系统间信息交换都需要建立在一定的数据格式基础上。另外，不管语言属于那种范式，设计上采用什么模式，在微观层次上程序很大一部分工作都是在做数据处理。所以，从数据处理角度比较和理解语言间的差异有重要的现实意义。虽然数据通常是平台和语言无关的，但不同的语言在处理某种格式的数据时会表现出不同的难度，甚至某些数据格式只能采用特定的语言才能实现，这就是数据亲和力的不同。&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;语言的数据亲和力(Data Affinity)指的是语言与某种数据格式之间的相容程度，它主要取决于语言的数据模型，类型系统，以及库的支持等。语言对某种数据格式亲和力越强，则操作某类数据越容易。&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;&amp;nbsp;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;font face="Lucida Console"&gt;二进制字节块格式&lt;/font&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;font face="Lucida Console"&gt;&lt;/font&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;font face="Lucida Console"&gt;&lt;/font&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;在偏底层的操作系统、嵌入式和通信系统中，二进制的字节块是最常见的一种数据格式。二进制数据布局紧凑和接近机器的特点使得它常常作为系统间通信或系统文件的数据格式。但一般高级语言不方便直接和0101打交道，而是基于记录、结构体和类等结构化表示操作数据，这就存在着在底层的二进制字节块和高层的结构化数据之间的转换问题。&lt;/font&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;C语言作为最主要的系统语言具有很高的字节块数据亲和力。这不仅因为C语言具有指针可以直接访问内存以外，还因为C的结构体(struct)可以和字节块建立起直接的映射关系。例如，在基于Socket连接的分布式系统中服务器端和客户端通过二进制的字节数据进行通信，通信双方只要事先定义共用的结构体，发送方先创建相应的结构体变量并填充字段，然后把变量对应的内存块copy到Socket，接收方从Socket读取字节块，然后把字节块强制类型转换为相应的结构体指针即可读取个字段信息。整个过程中通信的双方都没有复杂的信息编码和解码的过程。示例代码如下：&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040" face="Lucida Console"&gt;struct t_data {&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040" face="Lucida Console"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; int version;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040" face="Lucida Console"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; char type[10];&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040" face="Lucida Console"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; float value;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040" face="Lucida Console"&gt;};&lt;/font&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040" face="Lucida Console"&gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040" face="Lucida Console"&gt;//发送方&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040" face="Lucida Console"&gt;struct t_data data;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040" face="Lucida Console"&gt;data.version = 1;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040" face="Lucida Console"&gt;strcpy(data.type, &amp;#8220;degree&amp;#8221;);&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040" face="Lucida Console"&gt;data.value = 189.0;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040" face="Lucida Console"&gt;send(socket, &amp;nbsp;(char*)&amp;amp;data, sizeof(data));&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040" face="Lucida Console"&gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;font color="#800040" face="Lucida Console"&gt;//接收方&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040" face="Lucida Console"&gt;struct t_data data;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040" face="Lucida Console"&gt;read(socket, &amp;nbsp;(char*)&amp;amp;data, sizeof(data));&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040" face="Lucida Console"&gt;printf(&amp;#8220;%d, %s, %f&amp;#8221;, data.version, data.type, data.value);&lt;/font&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;上面的方法在实际应用中还需要注意内存对齐问题和大小端问题。内存对齐问题可以通过编译器预处理命令来进行控制，保证内存中struct结构与传输的字节块具有相同的对齐方式；大小端问题需要通信的双方采用同样的大小端方式，否则就需要进行转换。&lt;/font&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;C++可以完全兼容C的结构体，但C++的类(包括class和struct)中如果定义了虚函数，则会丧失结构的字节块数据亲和力，这是C++编程时需要权衡的一个因素。而除了C/C++，其他语言中则难以见到字节块数据亲和力，其原因在于C/C++允许控制结构体/对象的内存布局，并允许对指针进行非类型安全的强制类型转换，这都是在Java、Python等基于虚拟机的语言中不允许的。所以，在Java、Python中进行字节块的编码解码就只能按照协议一个字段一个字段地按偏移量和长度进行解析。C/C++的指针以及结构体和内存的直接映射带来了对字节块数据的亲和力，但同时也留下了内存访问和类型安全的隐患；而Java、Python等语言在拥有引用安全和类型安全的同时也失去了对字节块数据的亲和力。在基于虚拟机的语言中C#为struct提供了控制内存布局的能力，同时在unsafe模式下还进行直接的内存操作。&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;&amp;nbsp;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;font face="Lucida Console"&gt;&lt;/font&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;&lt;strong&gt;文本格式&lt;/strong&gt;&amp;nbsp;&lt;/font&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;文本格式是另一种十分常见的数据格式。《Unix编程艺术》是这样评价文本格式的："Text streams are a valuable universal format because they're easy for human beings to read, write, and edit without specialized tools &amp;#8221;。基于文本流的管道处理是一种备受赞誉的Unix风格。Shell可以通过管道把各种功能单一的命令串联起来，让文本流在管道上流动，因而Shell语言具有很好的文本数据亲和力。许多文本数据处理任务Bash都可以一行搞定，这就是Hacker们酷爱的One Liner风格。&lt;/font&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;下面我们来看两个用Bash进行文本处理的例子：&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;1. 统计当前目录下的gz文件数目：&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040" face="Lucida Console"&gt;ls &amp;#8211;l *.gz | wc &amp;#8211;l&lt;/font&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040" face="Lucida Console"&gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;2. 在Web服务器日志service.log中统计2011年6月26和27两天中每天各页面的PV&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040" face="Lucida Console"&gt;cat service.log | grep ^2011-06-2[6-7] | cut &amp;#8211;d &amp;#8216; &amp;#8216; &amp;#8211;f 1, 3 | sort | uniq &amp;#8211;c&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;service.log:&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;2011-06-25 13:00:55 /music/c.htm Safari&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;&amp;#8230;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;2011-06-26 08:01:23 /main.htm IE&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;2011-06-26 08:03:01 /sports/b.htm Chrome&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;&amp;#8230;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;2011-06-27 11:41:06 /main.htm IE&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;2011-06-27 11:52:41 /news/a.htm Firefox&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040" face="Lucida Console"&gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;输出:&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;210 2011-06-26 /main.htm      &lt;br /&gt;231 2011-06-26 /news/a.htm       &lt;br /&gt;155 2011-06-26 /sports/b.htm       &lt;br /&gt;288 2011-06-27 /main.htm       &lt;br /&gt;292 2011-06-27 /news/a.htm       &lt;br /&gt;161 2011-06-27 /sports/b.htm&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;&amp;nbsp;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;上面的两个简单文本数据处理任务如果是在C或C++下实现则要麻烦得多，代码量至少是十几行或者数十行，加上编译调试，整个开发效率可能比Shell低一个数量级。除了Shell外，Perl也是以强大的文本数据处理而闻名的。我们来看一个Perl正则表达式的例子：&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040" face="Lucida Console"&gt;while (&amp;lt;STDIN&amp;gt;) {&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040" face="Lucida Console"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (/hello\s(\w+)/i) {&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040" face="Lucida Console"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; print &amp;#8220;say hello to $1&amp;#8220; &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040" face="Lucida Console"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040" face="Lucida Console"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; elsif (/goodbye\s(\w+)/i) {&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040" face="Lucida Console"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; print &amp;#8220;say goodbye to $1&amp;#8221;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040" face="Lucida Console"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040" face="Lucida Console"&gt;}&lt;/font&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;输入：&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;HeLLo world&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;Goodbye bug&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;输出：&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;say hello to world&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;say goodbye to bug&lt;/font&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;上面的例子中我们看到Perl直接进行字符串匹配并进行数据提取的强大威力。Perl基于正则表达式的字符串处理不仅比C/C++等系统语言更强大，甚至比Python这样的动态语言也更强大和更方便，这是因为正则表达式是Perl语言的&amp;#8220;一等公民&amp;#8221;，这就使得Perl比其他以库的方式支持正则表达式功能的语言具有更好的文本数据亲和力。后来的Ruby也学习Perl直接在语言上支持正则表达式。&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;&amp;nbsp;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;font face="Lucida Console"&gt;结构化文本格式&lt;/font&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Lucida Console"&gt;XML是最近十几年来流行起来的一种通用（半）结构化的文本数据交换格式。XML除具有一般文本格式的优点外，还具有能层次结构表达力和可扩展性的优势，所以它至诞生以来就被大量用于配置文件和各种Web Service中。现代程序设计基本都少不了和XML打交道，不过在C++、Java和C#几种静态类型语言中处理XML却并不是一件十分轻松的事情。我们先来看一个Java解析和构建下面这个XML的例子：&lt;/font&gt;&lt;/p&gt;  &lt;pre&gt;&lt;font face="Lucida Console"&gt;&amp;lt;langs type="current"&amp;gt;&lt;br /&gt;  &amp;lt;language&amp;gt;Java&amp;lt;/language&amp;gt;&lt;br /&gt;  &amp;lt;language&amp;gt;Groovy&amp;lt;/language&amp;gt;&lt;br /&gt;  &amp;lt;language&amp;gt;JavaScript&amp;lt;/language&amp;gt;&lt;br /&gt;&amp;lt;/langs&amp;gt;&lt;/font&gt;&lt;/pre&gt;&#xD;
&#xD;
&lt;p&gt;&lt;font face="Lucida Console"&gt;&amp;nbsp;&lt;/font&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;font color="#800040" face="Lucida Console" size="2"&gt;//Java解析XML&lt;/font&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;pre&gt;&lt;font color="#800040" face="Lucida Console" size="2"&gt;DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();&lt;br /&gt;try {&lt;br /&gt;    DocumentBuilder db = dbf.newDocumentBuilder();&lt;br /&gt;    Document doc = db.parse("src/languages.xml");&lt;br /&gt;    Element langs = doc.getDocumentElement();&lt;br /&gt;    System.out.println("type = " + langs.getAttribute("type"));&lt;br /&gt;    NodeList list = langs.getElementsByTagName("language");&lt;br /&gt;    for(int i = 0 ; i &amp;lt; list.getLength();i++) {&lt;br /&gt;        Element language = (Element) list.item(i);&lt;br /&gt;        System.out.println(language.getTextContent());&lt;br /&gt;    }&lt;br /&gt;}catch(Exception e) {&lt;br /&gt;    e.printStackTrace();&lt;br /&gt;}&lt;/font&gt;&lt;font size="2"&gt;&#xD;
&lt;/font&gt;&lt;/pre&gt;&lt;pre&gt;&lt;font color="#800040" face="Lucida Console" size="2"&gt;&lt;br /&gt;&lt;/font&gt;&lt;/pre&gt;&#xD;
&#xD;
&lt;pre&gt;&lt;font color="#800040" face="Lucida Console"&gt;//Java创建XML&lt;/font&gt;&lt;/pre&gt;&#xD;
&#xD;
&lt;pre&gt;&lt;font color="#800040" face="Lucida Console"&gt;DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();&lt;br /&gt;try {&lt;br /&gt;    DocumentBuilder db = dbf.newDocumentBuilder();&lt;br /&gt;    Document doc = db.newDocument();&lt;br /&gt;    Element langs = doc.createElement("langs");&lt;br /&gt;    langs.setAttribute("type", "current");&lt;br /&gt;    doc.appendChild(langs);&lt;br /&gt;&lt;br /&gt;    Element language1 = doc.createElement("language");&lt;br /&gt;    Text text1 = doc.createTextNode("Java");&lt;br /&gt;    language1.appendChild(text1);&lt;br /&gt;    langs.appendChild(language1);&lt;br /&gt;&lt;br /&gt;    Element language2 = doc.createElement("language");&lt;br /&gt;    Text text2 = doc.createTextNode("Groovy");&lt;br /&gt;    language2.appendChild(text2);&lt;br /&gt;    langs.appendChild(language2);&lt;/font&gt;&lt;/pre&gt;&#xD;
&#xD;
&lt;pre&gt;&lt;font color="#800040" face="Lucida Console"&gt;    Element language3 = doc.createElement("language");&lt;br /&gt;    Text text3 = doc.createTextNode("JavaScript");&lt;br /&gt;    language3.appendChild(text3);&lt;br /&gt;    langs.appendChild(language3);&lt;br /&gt;&lt;/font&gt;&lt;font color="#800040" face="Lucida Console"&gt;} catch (Exception e) {&lt;br /&gt;    e.printStackTrace();&lt;/font&gt;&lt;/pre&gt;&#xD;
&#xD;
&lt;pre&gt;&lt;font color="#800040" face="Lucida Console"&gt;}&lt;/font&gt;&lt;/pre&gt;&lt;pre&gt;&lt;font color="#800040" face="Lucida Console"&gt;&lt;br /&gt;&lt;/font&gt;&lt;/pre&gt;&#xD;
&#xD;
&lt;pre&gt;&lt;font color="#800040" face="Lucida Console"&gt;&lt;/font&gt;&lt;/pre&gt;&#xD;
&#xD;
&lt;pre&gt;&lt;font color="#800040" face="Lucida Console"&gt;&lt;/font&gt;&lt;/pre&gt;&#xD;
&#xD;
&lt;pre&gt;&lt;font color="#800040" face="Lucida Console"&gt;&lt;/font&gt;&lt;/pre&gt;&#xD;
&#xD;
&lt;pre&gt;&lt;font color="#000040" face="Lucida Console"&gt;&lt;/font&gt;&lt;/pre&gt;&#xD;
&#xD;
&lt;pre&gt;&lt;font color="#000040" face="Lucida Console"&gt;为了解析和创建小小的一段XML代码需要编写如此冗长的Java代码，而实现同样的功能动态语言Groovy则十分简洁：&lt;/font&gt;&lt;/pre&gt;&#xD;
&#xD;
&lt;pre&gt;&lt;font color="#800040" face="Lucida Console"&gt;//Groovy解析XML			&lt;br /&gt;def langs = new XmlParser().parse("languages.xml")&lt;br /&gt;println "type = ${langs.attribute("type")}"&lt;br /&gt;langs.language.each{&lt;br /&gt;  println it.text()&lt;br /&gt;}&lt;/font&gt;&lt;/pre&gt;&lt;pre&gt;&lt;font color="#800040" face="Lucida Console"&gt;&lt;br /&gt;&lt;/font&gt;&lt;/pre&gt;&#xD;
&#xD;
&lt;pre&gt;&lt;font color="#800040" face="Lucida Console"&gt;&lt;/font&gt;&lt;/pre&gt;&#xD;
&#xD;
&lt;pre&gt;&lt;font color="#800040" face="Lucida Console"&gt;&lt;/font&gt;&lt;/pre&gt;&#xD;
&#xD;
&lt;pre&gt;&lt;font color="#800040" face="Lucida Console"&gt;&lt;/font&gt;&lt;/pre&gt;&#xD;
&#xD;
&lt;pre&gt;&lt;font color="#800040" face="Lucida Console"&gt;//Groovy创建XML&lt;/font&gt;&lt;/pre&gt;&#xD;
&#xD;
&lt;pre&gt;&lt;font color="#800040" face="Lucida Console"&gt;def xml = new groovy.xml.MarkupBuilder()&lt;br /&gt;xml.langs(type:"current"){&lt;br /&gt;  language("Java")&lt;br /&gt;  language("Groovy")&lt;br /&gt;  language("JavaScript")&lt;br /&gt;}&lt;/font&gt;&lt;/pre&gt;&#xD;
&#xD;
&lt;p&gt;&lt;font face="Lucida Console"&gt;&amp;nbsp;&lt;/font&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;font face="Lucida Console"&gt;上面Groovy操作XML的代码简洁而富有表达力，代码与XML几乎是一一对应的，如同直接在XML上进行操作的DSL一样，而相应的Java代码则看不到XML的影子。这说明Groovy具有很高的XML数据的亲和力。为什么Java和Groovy在XML亲和力方面有这样的差异呢？原因在于Java要求所有的方法和属性都必须先定义再调用，严格的静态类型检查使得Java只能把XML元素作为&amp;#8220;二等公民&amp;#8221;来表达；而Groovy则没有静态类型检查的限制，可以自由地使用方法和属性来表达XML结构。上面用Groovy创建XML的例子中，groovy.xml.MarkupBuilder类中实际上并没有langs, language这些方法，但会在调用的时候自动创建相应的XML结构。&lt;/font&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;font face="Lucida Console"&gt;除了XML外，JSON是另一种通用的半结构化的纯文本数据交换格式，它常被视为轻量级的XML。JSON的本意是Javascript的对象表示(Javascript Object Notation)，它属于Javascript的语法子集，Javascript对JSON有原生的支持。下面就是一个在Javascript中创建JSON对象的例子：&lt;/font&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;font color="#800040" face="Lucida Console" size="2"&gt;var json = { &amp;#8220;langs&amp;#8221; : { &lt;/font&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;font color="#800040" face="Lucida Console" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; "type&amp;#8221; : "current&amp;#8221;, &lt;/font&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;font color="#800040" face="Lucida Console" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; "language&amp;#8221; : ["Java&amp;#8221;, "Groovy&amp;#8221;, "Javascript&amp;#8221;]&lt;/font&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;font color="#800040" face="Lucida Console" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; } &lt;/font&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;font color="#800040" face="Lucida Console" size="2"&gt;}&lt;/font&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;font face="Lucida Console"&gt;许多Javascript程序都会通过AJAX都从服务器获取JSON字符串，然后把字符串解析为JSON对象。由于Javascript对JSON的原生支持，所以，在Javascript中解析JSON字符串可以采用通用的eval方式，如：&lt;/font&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;font color="#800040" face="Lucida Console" size="2"&gt;var json = eval(&amp;#8220;(" + jsonStr + &amp;#8220;)");&lt;/font&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;font color="#800040" face="Lucida Console" size="2"&gt;alert(json.langs.type);&lt;/font&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;font color="#0000ff" face="Lucida Console" size="2"&gt;&lt;/font&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;font color="#0000ff" face="Lucida Console" size="2"&gt;&lt;/font&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;font face="Lucida Console"&gt;甚至可以：&lt;/font&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;font color="#800040" face="Lucida Console" size="2"&gt;eval(&amp;#8220;var json = &amp;#8221; + jsonStr);&lt;/font&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;font color="#800040" face="Lucida Console" size="2"&gt;alert(json.langs.type);&lt;/font&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;font face="Lucida Console"&gt;不过eval的通用性带来了一定的安全隐患，所以一般只建议对受信任的数据源采用eval方式解析JSON，对于不受信任的数据源可以采用专门的JSON解析库。无论如何Javascript对JSON的原生支持都使得Javascript具有很高的JSON数据亲和力。另外，Groovy 1.8也加入了对JSON的原生支持，操作JSON与Javascript一样方便。&lt;/font&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;font face="Lucida Console"&gt;&lt;/font&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;font face="Lucida Console"&gt;&lt;/font&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;strong&gt;&lt;font face="Lucida Console"&gt;总结&lt;/font&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&#xD;
&#xD;
&lt;p&gt;&lt;font face="Lucida Console"&gt;到这里为止本文篇幅已经很长了，只能列举二进制字节块格式、文本格式和结构化文本格式3种典型的数据格式。实际上，数据亲和力的话题还有很多值得探讨的，比如C#的Linq。本文的探讨算是抛砖引玉，目的在于引起大家注意在比较语言的时候不要忽略了数据亲和力这样一个重要方面。本文的错误或不足，敬请指正，谢谢！&lt;/font&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/weidagang2046/aggbug/2091765.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/weidagang2046/archive/2011/06/27/data-affinity.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/weidagang2046/archive/2011/06/04/idempotence.html</id><title type="text">理解HTTP幂等性</title><summary type="text">在数学中，幂等性是指N次变换与1次变换的结果相同。本文介绍了：1.分布式系统中幂等性的概念；2.用幂等设计代替分布式事务的方法；3.HTTP主要方法的语义和幂等性。</summary><published>2011-06-04T12:51:00Z</published><updated>2011-06-04T12:51:00Z</updated><author><name>Todd Wei</name><uri>http://www.cnblogs.com/weidagang2046/</uri></author><link rel="alternate" href="http://www.cnblogs.com/weidagang2046/archive/2011/06/04/idempotence.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/weidagang2046/archive/2011/06/04/idempotence.html"/><content type="html">&lt;p&gt;&lt;font face="Gill Sans MT"&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333"&gt;基于&lt;font face="Gill Sans MT"&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333"&gt;HTTP&lt;/span&gt;&lt;/font&gt;&lt;/span&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333"&gt;协议的Web API是时下最为流行的一种分布式服务提供方式。无论是在大型互联网应用还是企业级架构中，我们都&lt;/span&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333"&gt;见到了&lt;/span&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333"&gt;越来越多&lt;/span&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333"&gt;的&lt;/span&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333"&gt;SOA或RESTful的Web API。为什么Web API如此流行呢？我认为&lt;/span&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333"&gt;很大程度上应&lt;/span&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333"&gt;归功于简单有效的&lt;font face="Gill Sans MT"&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333"&gt;HTTP&lt;/span&gt;&lt;/font&gt;&lt;/span&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333"&gt;协议&lt;/span&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333"&gt;。HTTP协议是一种&lt;/span&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333"&gt;分布式&lt;/span&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333"&gt;的面向资源的网络应用层&lt;/span&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333"&gt;协议&lt;/span&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333"&gt;，&lt;/span&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333"&gt;无论是服务器端提供Web服务，还是客户端消费Web服务都非常简单。&lt;/span&gt;&lt;span style="font-family: verdana"&gt;&lt;/span&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333"&gt;再加上浏览器、Javascript、AJAX、JSON以及HTML5等技术和工具的发展，互联网应用架构设计表现出了从传统的PHP、JSP、ASP.NET等服务器端动态网页向Web API + RIA（富互联网应用）过渡的趋势。Web API专注于提供业务服务，RIA专注于用户界面和交互设计，从此两个领域的分工更加明晰。在这种趋势下，Web API设计将成为服务器端程序员的必修课。&lt;/span&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333"&gt;然而，&lt;/span&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333"&gt;正如简单的Java语言并不意味着高质量的Java程序，简单的&lt;font face="Gill Sans MT"&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333"&gt;HTTP&lt;/span&gt;&lt;/font&gt;&lt;/span&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333"&gt;协议也不意味着高质量的&lt;font face="Gill Sans MT"&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333"&gt;Web API&lt;/span&gt;&lt;/font&gt;&lt;/span&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333"&gt;。&lt;/span&gt;&lt;span style="font-family: verdana"&gt;&lt;/span&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333"&gt;要想设计出高质量的&lt;font face="Gill Sans MT"&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333"&gt;Web API&lt;/span&gt;&lt;/font&gt;&lt;/span&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333"&gt;，还需要深入理解分布式系统及&lt;font face="Gill Sans MT"&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333"&gt;HTTP&lt;/span&gt;&lt;/font&gt;&lt;/span&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333"&gt;协议的特性。&lt;/span&gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Gill Sans MT"&gt;&amp;nbsp;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;span style="font-family: verdana" &gt;&lt;font face="Gill Sans MT"&gt;&lt;/font&gt;&lt;/span&gt;&lt;/p&gt;  &lt;p&gt;&lt;span style="font-family: verdana" &gt;&lt;strong&gt;&lt;font face="Gill Sans MT"&gt;&lt;/font&gt;&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;  &lt;p&gt;&lt;span style="font-family: verdana" &gt;&lt;strong&gt;&lt;font face="Gill Sans MT"&gt;&lt;/font&gt;&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;  &lt;p&gt;&lt;span style="font-family: verdana" &gt;&lt;strong&gt;&lt;font face="Gill Sans MT"&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333"&gt;幂等性定义&lt;/span&gt;&lt;/font&gt;&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Gill Sans MT"&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333" &gt;本文所要探讨的正是HTTP协议涉及到的一种重要性质：幂等性(&lt;font face="Gill Sans MT"&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333"&gt;Idempotence&lt;/span&gt;&lt;/font&gt;&lt;/span&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333" &gt;)。&lt;/span&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333" &gt;在HTTP/1.1规范中幂等性的定义是：&lt;/span&gt;&lt;/font&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;span style="font-family: verdana" &gt;&lt;/span&gt;&lt;span style="line-height: normal; font-family: verdana" &gt;&lt;font color="#800040" face="Gill Sans MT"&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333"&gt;Methods can also have the property of "idempotence" in that (aside from error or expiration issues) the side-effects of N &amp;gt; 0 identical requests is the same as for a single request.&lt;/span&gt;&lt;/font&gt;&lt;/span&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Gill Sans MT"&gt;&amp;nbsp;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;span style="line-height: normal; font-family: verdana" &gt;&lt;font face="Gill Sans MT"&gt;&lt;/font&gt;&lt;/span&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Gill Sans MT"&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333" &gt;从定义上看，HTTP方法的幂等性是指一次和多次请求某一个资源应该具有同样的副作用。幂等性属于语义范畴，正如编译器只能帮助检查语法错误一样，HTTP规范也没有办法通过消息格式等语法手段来定义它，这可能是它不太受到重视的原因之一。但实际上，幂等性是分布式系统设计中十分重要&lt;/span&gt;&lt;span style="line-height: normal; font-family: 'Lucida Console'; color: #333333" &gt;的&lt;/span&gt;&lt;span style="line-height: normal; font-family: 'Lucida Console'; color: #333333" &gt;概念，而HTTP的分布式本质也决定了它在HTTP中具有重要地位。&lt;/span&gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Gill Sans MT"&gt;&amp;nbsp;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;span style="line-height: normal; font-family: verdana" &gt;&lt;font face="Gill Sans MT"&gt;&lt;/font&gt;&lt;/span&gt;&lt;/p&gt;  &lt;p&gt;&lt;span style="line-height: normal; font-family: verdana" &gt;&lt;strong&gt;&lt;font face="Gill Sans MT"&gt;&lt;/font&gt;&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;  &lt;p&gt;&lt;span style="line-height: normal; font-family: verdana" &gt;&lt;strong&gt;&lt;font face="Gill Sans MT"&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333"&gt;分布式事务 vs 幂等设计&lt;/span&gt;&lt;/font&gt;&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Gill Sans MT"&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333"&gt;为什么需要幂等性呢？我们先从一个例子说起，假设有一个从账户取钱的远程API（可以是HTTP的，也可以不是），我们暂时用类函数的方式记为：&lt;/span&gt;&lt;/font&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040" face="Gill Sans MT"&gt;&lt;span style="font-family: 'Lucida Console'; color: #333300"&gt;bool withdraw(account_id, amount)&lt;/span&gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Gill Sans MT"&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333"&gt;withdraw的语义是从account_id对应的账户中扣除amount数额的钱；如果扣除成功则返回true，账户余额减少amount；如果扣除失败则返回false，账户余额不变。值得注意的是：和本地环境相比，我们不能轻易假设分布式环境的可靠性。一种典型的情况是withdraw请求已经被服务器端正确处理，但服务器端的返回结果由于网络等原因被掉丢了，导致客户端无法得知处理结果。如果是在网页上，一些不恰当的设计可能会使用户认为上一次操作失败了，然后刷新页面，这就导致了withdraw被调用两次，账户也被多扣了一次钱。如图1所示：&lt;/span&gt;&lt;/font&gt;&lt;/p&gt;  &lt;p align="center"&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/weidagang2046/201108/201108101715033116.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="non-idemponent" border="0" alt="non-idemponent" src="http://images.cnblogs.com/cnblogs_com/weidagang2046/201108/20110810171503575.png" width="416" height="356" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p align="center"&gt;&lt;font face="Gill Sans MT"&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333"&gt;图1 &lt;/span&gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Gill Sans MT"&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333"&gt;这个问题的解决方案一是采用分布式事务，通过引入支持分布式事务的中间件来保证withdraw功能的事务性。分布式事务的优点是对于调用者很简单，复杂性都交给了中间件来管理。缺点则是一方面架构太重量级，容易被绑在特定的中间件上，不利于异构系统的集成；另一方面分布式事务虽然能保证事务的ACID性质，而但却无法提供性能和可用性的保证。&lt;/span&gt;&lt;/font&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Gill Sans MT"&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333"&gt;另一种更轻量级的解决方案是幂等设计。我们可以通过一些技巧把withdraw变成幂等的，比如：&lt;/span&gt;&lt;/font&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt; &lt;font color="#800040" face="Gill Sans MT"&gt;&lt;span style="font-family: 'Lucida Console'; color: #333300"&gt;int create_ticket()      &lt;br /&gt;&lt;/span&gt;&lt;/font&gt;  &lt;p&gt;&lt;font color="#800040" face="Gill Sans MT"&gt;&lt;span style="font-family: 'Lucida Console'; color: #333300"&gt;bool idempotent_withdraw(ticket_id, account_id, amount)&lt;/span&gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Gill Sans MT"&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333"&gt;create_ticket的语义是获取一个服务器端生成的唯一的处理号ticket_id，它将用于标识后续的操作。idempotent_withdraw和withdraw的区别在于关联了一个ticket_id，一个ticket_id表示的操作至多只会被处理一次，每次调用都将返回第一次调用时的处理结果。这样，idempotent_withdraw就符合幂等性了，客户端就可以放心地多次调用。&lt;/span&gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Gill Sans MT"&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333"&gt;基于幂等性的解决方案中一个完整的取钱流程被分解成了两个步骤：1.调用create_ticket()获取ticket_id；2.调用idempotent_withdraw(ticket_id, account_id, amount)。虽然create_ticket不是幂等的，但在这种设计下，它对系统状态的影响可以忽略，加上idempotent_withdraw是幂等的，所以任何一步由于网络等原因失败或超时，客户端都可以重试，直到获得结果。如图2所示：&lt;/span&gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/weidagang2046/201106/201106042051059820.png"&gt;&lt;font face="Gill Sans MT"&gt;&lt;img style="border-right-width: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/weidagang2046/201106/201106042051069339.png" width="570" height="541" /&gt;&lt;/font&gt;&lt;/a&gt;&lt;font face="Gill Sans MT"&gt; &lt;/font&gt;&lt;/p&gt;  &lt;p align="center"&gt;&lt;font face="Gill Sans MT"&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333"&gt;图2&lt;/span&gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;font face="Gill Sans MT"&gt;&lt;/font&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Gill Sans MT"&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333"&gt;和分布式事务相比，幂等设计的优势在于它的轻量级，容易适应异构环境，以及性能和可用性方面。在某些性能要求比较高的应用，幂等设计往往是唯一的选择。&lt;/span&gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Gill Sans MT"&gt;&amp;nbsp;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;font face="Gill Sans MT"&gt;&lt;/font&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;font face="Gill Sans MT"&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333"&gt;HTTP的幂等性&lt;/span&gt;&lt;/font&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Gill Sans MT"&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333"&gt;HTTP协议本身是一种面向资源的应用层协议，但对HTTP协议的使用实际上存在着两种不同的方式：一种是RESTful的，它把HTTP当成应用层协议，比较忠实地遵守了HTTP协议的各种规定；另一种是SOA的，它并没有完全把HTTP当成应用层协议，而是把HTTP协议作为了传输层协议，然后在HTTP之上建立了自己的应用层协议。本文所讨论的HTTP幂等性主要针对RESTful风格的，不过正如上一节所看到的那样，幂等性并不属于特定的协议，它是分布式系统的一种特性；所以，不论是SOA还是RESTful的Web API设计都应该考虑幂等性。下面将介绍HTTP GET、DELETE、PUT、POST四种主要方法的语义和幂等性。&lt;/span&gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Gill Sans MT"&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333"&gt;HTTP GET方法用于获取资源，不应有副作用，所以是幂等的。比如：GET http://www.bank.com/account/123456，不会改变资源的状态，不论调用一次还是N次都没有副作用。请注意，这里强调的是一次和N次具有相同的副作用，而不是每次GET的结果相同。GET http://www.news.com/latest-news这个HTTP请求可能会每次得到不同的结果，但它本身并没有产生任何副作用，因而是满足幂等性的。&lt;/span&gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Gill Sans MT"&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333"&gt;HTTP DELETE方法用于删除资源，有副作用，但它应该满足幂等性。比如：DELETE http://www.forum.com/article/4231，调用一次和N次对系统产生的副作用是相同的，即删掉id为4231的帖子；因此，调用者可以多次调用或刷新页面而不必担心引起错误。&lt;/span&gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Gill Sans MT"&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333"&gt;比较容易混淆的是HTTP POST和PUT。POST和PUT的区别容易被简单地误认为&amp;#8220;POST表示创建资源，PUT表示更新资源&amp;#8221;；而实际上，二者均可用于创建资源，更为本质的差别是在幂等性方面。在HTTP规范中对POST和PUT是这样定义的：&lt;/span&gt;&lt;/font&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040"&gt;&lt;font face="Gill Sans MT"&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333"&gt;The POST method is used to request that the origin server accept the entity enclosed in the request as a new subordinate of the resource identified by the Request-URI in the Request-Line. ......&lt;/span&gt;&lt;em style="font-family: 'Lucida Console'; color: #333333"&gt; &lt;/em&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333; font-size: 10pt"&gt;If a resource has been created on the origin server, the response SHOULD be 201 (Created) and contain an entity which describes the status of the request and refers to the new resource, and a Location header. &lt;/span&gt;&lt;/font&gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#800040" face="Gill Sans MT"&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333"&gt;The PUT method requests that the enclosed entity be stored under the supplied Request-URI. If the Request-URI refers to an already existing resource, the enclosed entity SHOULD be considered as a modified version of the one residing on the origin server. If the Request-URI does not point to an existing resource, and that URI is capable of being defined as a new resource by the requesting user agent, the origin server can create the resource with that URI. &lt;/span&gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Gill Sans MT"&gt;     &lt;br /&gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Gill Sans MT"&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333"&gt;POST所对应的URI并非创建的资源本身，而是资源的接收者。比如：POST http://www.forum.com/articles的语义是在http://www.forum.com/articles下创建一篇帖子，HTTP响应中应包含帖子的创建状态以及帖子的URI。两次相同的POST请求会在服务器端创建两份资源，它们具有不同的URI；所以，POST方法不具备幂等性。&lt;/span&gt;&lt;/font&gt;&lt;span  style="color: #333333; font-family: 'Lucida Console'; "&gt;而PUT所对应的URI是要创建或更新的资源本身。比如：PUT http://www.forum/articles/4231的语义是创建或更新ID为4231的帖子。对同一URI进行多次PUT的副作用和一次PUT是相同的；因此，PUT方法具有幂等性。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Gill Sans MT"&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333"&gt;在介绍了几种操作的语义和幂等性之后，我们来看看如何通过Web API的形式实现前面所提到的取款功能。很简单，用POST /tickets来实现create_ticket；用PUT /accounts/account_id/ticket_id&amp;amp;amount=xxx来实现idempotent_withdraw。值得注意的是严格来讲amount参数不应该作为URI的一部分，真正的URI应该是/accounts/account_id/ticket_id，而amount应该放在请求的body中。这种模式可以应用于很多场合，比如：论坛网站中防止意外的重复发帖。&lt;/span&gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;font face="Gill Sans MT"&gt;&lt;/font&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Gill Sans MT"&gt;&amp;nbsp;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;font face="Gill Sans MT"&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333"&gt;总结&lt;/span&gt;&lt;/font&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Gill Sans MT"&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333"&gt;上面简单介绍了幂等性的概念，用幂等设计取代分布式事务的方法，以及HTTP主要方法的语义和幂等性特征。其实，如果要追根溯源，幂等性是数学中的一个概念，表达的是N次变换与1次变换的结果相同，有兴趣的读者可以从&lt;/span&gt;&lt;/font&gt;&lt;a href="http://en.wikipedia.org/wiki/Idempotence"&gt;&lt;font face="Gill Sans MT"&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333"&gt;Wikipedia&lt;/span&gt;&lt;/font&gt;&lt;/a&gt;&lt;font face="Gill Sans MT"&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333"&gt;上进一步了解。&lt;/span&gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;font face="Gill Sans MT"&gt;&lt;/font&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Gill Sans MT"&gt;&amp;nbsp;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;font face="Gill Sans MT"&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333"&gt;参考&lt;/span&gt;&lt;/font&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html"&gt;&lt;font face="Gill Sans MT"&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333"&gt;RFC 2616, Hypertext Transfer Protocol -- HTTP/1.1, Method Definitions&lt;/span&gt;&lt;/font&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://devhawk.net/2007/11/09/the-importance-of-idempotence/"&gt;&lt;font face="Gill Sans MT"&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333"&gt;The Importance of Idempotence&lt;/span&gt;&lt;/font&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Gill Sans MT"&gt;&lt;a href="http://stackoverflow.com/questions/630453/put-vs-post-in-rest"&gt;&lt;span style="font-family: 'Lucida Console'; color: #333333"&gt;stackoverflow -&amp;nbsp; PUT vs POST in REST&lt;/span&gt;&lt;/a&gt;&lt;/font&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/weidagang2046/aggbug/2063696.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/weidagang2046/archive/2011/06/04/idempotence.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/weidagang2046/archive/2011/04/05/2005731.html</id><title type="text">Linux小技巧</title><summary type="text">汇集了一些bash和linux的小技巧，方便自己查阅</summary><published>2011-04-05T04:46:00Z</published><updated>2011-04-05T04:46:00Z</updated><author><name>Todd Wei</name><uri>http://www.cnblogs.com/weidagang2046/</uri></author><link rel="alternate" href="http://www.cnblogs.com/weidagang2046/archive/2011/04/05/2005731.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/weidagang2046/archive/2011/04/05/2005731.html"/><content type="html">&lt;p&gt;&lt;strong&gt;1. 用&amp;amp;&amp;amp; ||简化if else      &lt;br /&gt;&lt;/strong&gt;gzip -t a.tar.gz     &lt;br /&gt;if [[ 0 == $? ]]; then     &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; echo &amp;quot;good zip&amp;quot;     &lt;br /&gt;else     &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; echo &amp;quot;bad zip&amp;quot;     &lt;br /&gt;fi     &lt;br /&gt;可以简化为：     &lt;br /&gt;gzip -t a.tar.gz &amp;amp;&amp;amp; echo &amp;quot;good zip&amp;quot; || echo &amp;quot;bad zip&amp;quot; &lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;2. 命令行参数解析      &lt;br /&gt;&lt;/strong&gt;while getopts &amp;quot;:a:b:c&amp;quot; OPT; do     &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; case $OPT in     &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; a) arg_a=$OPTARG&amp;quot;;;     &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; b) arg_b=$OPTARG;;     &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; c) arg_c=true;;     &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; ?) ;;     &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; esac     &lt;br /&gt;done     &lt;br /&gt;shift $((OPTIND-1)) &lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;3. 获取文件大小      &lt;br /&gt;&lt;/strong&gt;$ stat -c %s fw8ben.pdf &lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;4. 字符串替换      &lt;br /&gt;&lt;/strong&gt;替换第一个：${string//pattern/replacement}     &lt;br /&gt;替换全部：${string//pattern/replacement}     &lt;br /&gt;$ a='a,b,c'     &lt;br /&gt;$ echo ${a//,/ /}     &lt;br /&gt;a b c &lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;5. Contains子字符串？&lt;/strong&gt;     &lt;br /&gt;string='My string'     &lt;br /&gt;if [[ $string == *My* ]]; then     &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; echo &amp;quot;It's there!&amp;quot;     &lt;br /&gt;fi &lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;6. 重定向      &lt;br /&gt;&lt;/strong&gt;... 1&amp;gt;File 2&amp;gt;&amp;amp;1 &lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;7. 备份      &lt;br /&gt;&lt;/strong&gt;rsync -r -t -v /source_folder /destination_folder     &lt;br /&gt;rsync -r -t -v /source_folder [user@]host:/destination_folder&amp;#160; &lt;br /&gt;注：命令执行后destination_folder内将包含一个名为source_folder的目录。 &lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;8. 批量rename      &lt;br /&gt;&lt;/strong&gt;为所有的txt文件加上.bak后缀：     &lt;br /&gt;rename '.txt' '.txt.bak' *.txt     &lt;br /&gt;去掉所有的bak后缀：     &lt;br /&gt;rename '.bak' '' *.bak &lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;9. 字符集设置      &lt;br /&gt;&lt;/strong&gt;echo $LANG     &lt;br /&gt;/etc/sysconfig/i18n &lt;/p&gt;  &lt;p&gt;&lt;br /&gt;&lt;strong&gt;10. for/while循环      &lt;br /&gt;&lt;/strong&gt;for ((i=0; i &amp;lt; 10; i++)); do echo $i; done     &lt;br /&gt;for line in $(cat a.txt); do echo $line; done     &lt;br /&gt;for f in *.txt; do echo $f; done     &lt;br /&gt;while read line ; do echo $line; done &amp;lt; a.txt     &lt;br /&gt;cat a.txt | while read line; do echo $line; done &lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;11. 进程终止      &lt;br /&gt;&lt;/strong&gt;pkill swiftfox #根据名称终止进程     &lt;br /&gt;kill -9 &amp;lt;pid&amp;gt; #根据pid终止进程 &lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;12. find      &lt;br /&gt;&lt;/strong&gt;find ~/tmp -name &amp;quot;*abc*.txt&amp;quot; -mtime -5 #在~/tmp目录下查找名为*abc*.txt且修改时间为5天内的文件 &lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;13. 删除空行      &lt;br /&gt;&lt;/strong&gt;cat a.txt | sed -e '/^$/d'     &lt;br /&gt;$ (echo &amp;quot;abc &amp;quot;; echo &amp;quot;&amp;quot;; echo &amp;quot;ddd&amp;quot;;) | awk '{ if(0!=NF) print $0;}'&lt;/p&gt;  &lt;p&gt;&lt;br /&gt;&lt;strong&gt;14. 比较文件修改时间      &lt;br /&gt;&lt;/strong&gt;[[ file1.txt -nt file2.txt ]] &amp;amp;&amp;amp; echo true || echo false #-nt means &amp;quot;newer than&amp;quot; &lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;15. 定时关机      &lt;br /&gt;&lt;/strong&gt;nohup shutdown -t 10 +30 &amp;amp;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;br /&gt;#-t 10: warning与kill signal的间隔时间10s；+30: 分钟后定时关机 &lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;16. 模式提取      &lt;br /&gt;&lt;/strong&gt;$echo '2011-07-15 server_log_123.log hello world' | grep -o 'server_log_[0-9]\+\.log'&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;br /&gt;server_log_123.log     &lt;br /&gt;$echo '2011-07-15 server_log_123.log hello world' | sed 's/.*\(server_log_.*\.log\).*/\1/'&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;br /&gt;server_log_123.log &lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;17. DOS转Unix      &lt;br /&gt;&lt;/strong&gt;$cat a.txt | tr -d '\r'     &lt;br /&gt;$dos2unix a.txt&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;18.实现Dictionary结构&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;hput() {    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; eval &amp;quot;hkey_$1&amp;quot;=&amp;quot;$2&amp;quot;     &lt;br /&gt;} &lt;/p&gt;  &lt;p&gt;hget() {    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; eval echo '${'&amp;quot;hkey_$1&amp;quot;'}'     &lt;br /&gt;}&lt;/p&gt;  &lt;p&gt;$hput k1 aaa&lt;/p&gt;  &lt;p&gt;$hget k1&lt;/p&gt;  &lt;p&gt;aaa&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;19.去掉第二列&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;$echo 'a b c d e f' | cut -d ' ' -f1,3- &lt;/p&gt;  &lt;p&gt;a c d e f&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;20.把stderr输出保存到变量&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;$ a=$( (echo 'out'; echo 'error' 1&amp;gt;&amp;amp;2) 2&amp;gt;&amp;amp;1 1&amp;gt;/dev/null)&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;$ echo $a&lt;/p&gt;  &lt;p&gt;error&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;21.删除前3行&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;$cat a.txt | sed 1,3d&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;22.大小写转换&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;$ echo $foo | tr '[:lower:]' '[:upper:]'&lt;/p&gt;  &lt;p&gt;$ tr ‘[A-Z]’ ‘[a-z']’ &amp;lt; foo.txt&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;23.读取多个域到变量&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;$ read a b c &amp;lt;&amp;lt;&amp;lt; “xxx yyy zzz”&lt;/p&gt;  &lt;p&gt;$ echo $b&lt;/p&gt;  &lt;p&gt;yyy&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;24.遍历数组&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;array=( one two three )    &lt;br /&gt;for i in ${array[@]}     &lt;br /&gt;do     &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160; echo $i     &lt;br /&gt;done&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;25.查看硬盘使用情况&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;$ df –h &lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;26.查看目录大小&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;$ du –sh ~/apps&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;27.查看CPU信息&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;$ cat /proc/cpuinfo&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;28.date&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;$ date +%Y-%m-%d&lt;/p&gt;  &lt;p&gt;2012-12-24&lt;/p&gt;  &lt;p&gt;$ date +%Y-%m-%d –date ‘-1 day’&lt;/p&gt;  &lt;p&gt;2012-12-23&lt;/p&gt;  &lt;p&gt;$ date +%Y-m-%d –date ‘Dec 25’&lt;/p&gt;  &lt;p&gt;2011-12-25&lt;/p&gt;  &lt;p&gt;$ date +%Y-m-%d –date ‘Dec 25 – 10 days’&lt;/p&gt;  &lt;p&gt;2011-12-15&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;29.svn&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;1) 启动svn daemon&lt;/p&gt;  &lt;p&gt;$ svnserve –d –r &amp;lt;svn_home&amp;gt;&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;30.添加sudoers&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;$ vim /etc/sudoers&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;31.获取路径名和文件名&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;$ dirname ‘/home/todd/a.txt’&lt;/p&gt;  &lt;p&gt;/home/todd&lt;/p&gt;  &lt;p&gt;$ basename ‘/home/todd/a.txt’&lt;/p&gt;  &lt;p&gt;a.txt&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;32. 查看用户最近登录时间&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;$ finger weidagang&lt;/p&gt;  &lt;p&gt;[weidagang@localhost ~]$ finger weidagang    &lt;br /&gt;Login: weidagang&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; Name: weidagang     &lt;br /&gt;Directory: /home/weidagang&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; Shell: /bin/bash     &lt;br /&gt;On since Sat Jan&amp;#160; 7 14:15 (CST) on pts/2 from 10.69.22.141&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;33.常用网络配置文件和命令&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;[网卡]&lt;/p&gt;  &lt;p&gt;配置文件：/etc/sysconfig/network-scripts/ifcfg-&amp;lt;i/f name&amp;gt;&lt;/p&gt;  &lt;p&gt;&lt;em&gt;&amp;lt;静态IP分配实例&amp;gt;&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;&lt;em&gt;&lt;tt&gt;DEVICE=eth0&lt;/tt&gt;       &lt;br /&gt;&lt;tt&gt;ONBOOT=yes&lt;/tt&gt;       &lt;br /&gt;&lt;tt&gt;BOOTPROTO=static&lt;/tt&gt;       &lt;br /&gt;&lt;tt&gt;IPADDR=192.168.168.11&lt;/tt&gt;       &lt;br /&gt;&lt;tt&gt;NETMASK=255.255.255.0&lt;/tt&gt;       &lt;br /&gt;&lt;tt&gt;GATEWAY=192.168.168.252&lt;/tt&gt;&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;&lt;tt&gt;&lt;em&gt;&amp;lt;DHCP实例&amp;gt;&lt;/em&gt;&lt;/tt&gt;&lt;/p&gt;  &lt;p&gt;&lt;em&gt;&lt;tt&gt;ONBOOT=yes&lt;/tt&gt;       &lt;br /&gt;&lt;tt&gt;TYPE=Ethernet&lt;/tt&gt;       &lt;br /&gt;&lt;tt&gt;DHCP_HOSTNAME=sleipnir.cullen.lesbell.com.au&lt;/tt&gt;       &lt;br /&gt;&lt;tt&gt;DEVICE=eth0&lt;/tt&gt;       &lt;br /&gt;&lt;tt&gt;HWADDR=00:0c:6e:0a:3d:26&lt;/tt&gt;       &lt;br /&gt;&lt;tt&gt;BOOTPROTO=dhcp&lt;/tt&gt;       &lt;br /&gt;&lt;tt&gt;USERCTL=no&lt;/tt&gt;       &lt;br /&gt;&lt;tt&gt;PEERDNS=yes&lt;/tt&gt;&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;&lt;tt&gt;&lt;/tt&gt;&lt;/p&gt;  &lt;p&gt;查看网卡状态：ifconfig&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;改变网络状态：&lt;/p&gt;  &lt;p&gt;&amp;lt;CentOS&amp;gt;&lt;/p&gt;  &lt;p&gt;service network {start|stop|restart|reload|status} 或 /etc/init.d/network {start|stop|restart|reload|status}&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&amp;lt;Ubuntu&amp;gt;&lt;/p&gt;  &lt;p&gt;/etc/init.d/networking restart&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;[DNS]&lt;/p&gt;  &lt;p&gt;配置文件：/etc/resolv.conf&lt;/p&gt;  &lt;p&gt;&lt;em&gt;&lt;tt&gt;search cullen.lesbell.com.au lesbell.com.au&lt;/tt&gt;       &lt;br /&gt;&lt;tt&gt;nameserver 192.168.168.1&lt;/tt&gt;       &lt;br /&gt;&lt;tt&gt;nameserver 192.168.168.252&lt;/tt&gt;&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;&lt;em&gt;&lt;tt&gt;&lt;/tt&gt;&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;&lt;tt&gt;查看域名解析结果：nslookup&lt;/tt&gt;&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;tt&gt;&lt;/tt&gt;&lt;/p&gt;  &lt;p&gt;&lt;tt&gt;&lt;/tt&gt;&lt;/p&gt;  &lt;p&gt;&lt;tt&gt;&lt;/tt&gt;&lt;/p&gt;  &lt;p&gt;&lt;tt&gt;&lt;/tt&gt;&lt;/p&gt;  &lt;p&gt;&lt;tt&gt;[Host]&lt;/tt&gt;&lt;/p&gt;  &lt;p&gt;&lt;tt&gt;配置文件：/etc/hosts&lt;/tt&gt;&lt;/p&gt;  &lt;p&gt;&lt;tt&gt;&lt;em&gt;192.168.0.15 todd&lt;/em&gt;&lt;/tt&gt;&lt;/p&gt;  &lt;p&gt;&lt;tt&gt;&lt;em&gt;&lt;/em&gt;&lt;/tt&gt;&lt;/p&gt;  &lt;p&gt;&lt;tt&gt;&lt;em&gt;&lt;/em&gt;&lt;/tt&gt;&lt;/p&gt;  &lt;p&gt;&lt;tt&gt;&lt;/tt&gt;&lt;/p&gt;  &lt;p&gt;&lt;tt&gt;&lt;/tt&gt;&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;tt&gt;[ARP]&lt;/tt&gt;&lt;/p&gt;  &lt;p&gt;&lt;tt&gt;查看arp cache命令：arp –a&lt;/tt&gt;&lt;/p&gt;  &lt;p&gt;&lt;tt&gt;&lt;/tt&gt;&lt;/p&gt;  &lt;p&gt;&lt;tt&gt;&lt;/tt&gt;&lt;/p&gt;  &lt;p&gt;&lt;tt&gt;&lt;/tt&gt;&lt;/p&gt;  &lt;p&gt;&lt;tt&gt;&lt;/tt&gt;&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;tt&gt;[路由]&lt;/tt&gt;&lt;/p&gt;  &lt;p&gt;&lt;tt&gt;查看路由：netstat -nr&lt;/tt&gt;&lt;/p&gt;  &lt;p&gt;&lt;tt&gt;查看和修改路由表命令：route&lt;/tt&gt;&lt;/p&gt;  &lt;p&gt;&lt;tt&gt;跟踪路由：traceroute&lt;/tt&gt;&lt;/p&gt;  &lt;div style="display: none; right: 150px; opacity: 0" &gt;&lt;/div&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;34. 查看占用某端口的进程ID和程序名&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;$ netstat --tcp -l -n -p | grep 3306&lt;/p&gt;  &lt;p&gt;tcp&amp;#160;&amp;#160;&amp;#160; 0&amp;#160;&amp;#160;&amp;#160; 0&amp;#160;&amp;#160;&amp;#160; 127.0.0.1:3306&amp;#160;&amp;#160;&amp;#160; 0.0.0.0:*&amp;#160;&amp;#160;&amp;#160; LISTEN&amp;#160;&amp;#160;&amp;#160; 27238/mysqld&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;35. 查看和修改主机名&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;$ echo $HOSTNAME&lt;/p&gt;  &lt;p&gt;$ echo /proc/sys/kernel/hostname&lt;/p&gt;  &lt;p&gt;$ vim /etc/sysconfig/network&lt;/p&gt;&lt;img src="http://www.cnblogs.com/weidagang2046/aggbug/2005731.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/weidagang2046/archive/2011/04/05/2005731.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/weidagang2046/archive/2011/03/01/1967106.html</id><title type="text">软件需求的薛定谔之猫</title><summary type="text">薛定谔的猫（Erwin Schrodinger&amp;#39;s Cat）是奥地利物理学家埃尔温&amp;#183;薛定谔试图证明量子力学在宏观条件下的不完备性而提出的一个思想实验。实验内容如下：“把一只猫放进一个封闭的盒子里，然后把这个盒子连接到一个包含一个放射性原子核和一个装有有毒气体的容器的实验装置。设想这个放射性原子核在一个小时内有50％的可能性发生衰变。如果发生衰变，它将会发射出一个粒子，而发射出的这个粒子将会触发这个实验装置，打开装有毒气的容器，从而杀死这只猫。根据量子力学，未进行观察时，这个原子核处于已衰变和未衰变的叠加态，但是，如果在一个小时后把盒子打开，实验者只能看到“衰变的原子核和死猫”</summary><published>2011-03-01T07:05:00Z</published><updated>2011-03-01T07:05:00Z</updated><author><name>Todd Wei</name><uri>http://www.cnblogs.com/weidagang2046/</uri></author><link rel="alternate" href="http://www.cnblogs.com/weidagang2046/archive/2011/03/01/1967106.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/weidagang2046/archive/2011/03/01/1967106.html"/><content type="html">&lt;div&gt;&lt;span style="font-family: Verdana; "&gt;薛定谔的猫（Erwin Schrodinger's Cat）是奥地利物理学家埃尔温&amp;#183;薛定谔试图证明量子力学在宏观条件下的不完备性而提出的一个思想实验。实验内容如下：&lt;/span&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;div align="center"&gt;&lt;img alt="" src="http://images.cnblogs.com/cnblogs_com/weidagang2046/schrodingers-cat.png" border="0" height="235" width="442" /&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&amp;#8220;把一只猫放进一个封闭的盒子里，然后把这个盒子连接到一个包含一个放射性原子核和一个装有有毒气体的容器的实验装置。设想这个放射性原子核在一个小时内有50％的可能性发生衰变。如果发生衰变，它将会发射出一个粒子，而发射出的这个粒子将会触发这个实验装置，打开装有毒气的容器，从而杀死这只猫。根据量子力学，未进行观察时，这个原子核处于已衰变和未衰变的叠加态，但是，如果在一个小时后把盒子打开，实验者只能看到&amp;#8220;衰变的原子核和死猫&amp;#8221;或者&amp;#8220;未衰变的原子核和活猫&amp;#8221;两种情况。现在的问题是：这个系统从什么时候开始不再处于两种不同状态的叠加态而成为其中的一种？在打开盒子观察以前，这只猫是死了还是活着抑或半死半活？这个实验的原意是想说明，如果不能对波函数塌缩以及对这只猫所处的状态给出一个合理解释的话，量子力学本身是不完备的。&lt;/span&gt;&lt;span&gt;&amp;#8221;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family: Verdana; "&gt;根据哥本哈根学派的解释，当观察者未打开盒子之前，猫处于一种&amp;#8220;又死又活&amp;#8221;的状态，该状态可以用一个波函数来描述，而波函数可由薛定谔方程解出。一旦观察者打开盒子观察，波函数会坍塌，猫呈现在观察者面前的只会是&amp;#8220;生&amp;#8221;或&amp;#8220;死&amp;#8221;的状态之一，&lt;/span&gt;&lt;strong style="font-family: Verdana; "&gt;似乎猫的生死是在打开盒子那一瞬间才确定的&lt;/strong&gt;&lt;span style="color: #000080;"&gt;&lt;/span&gt;。这导致了对世界客观性和人意识的作用的讨论。&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family: Verdana; "&gt;我对理论物理没有研究，也不打算就薛定谔的猫做更深入地了解，本文只想借&amp;#8220;薛定谔的猫&amp;#8221;做一个隐喻，把它和生活中的一类&amp;#8220;不确定性&amp;#8221;现象联系起来。我们先来看几个例子：&lt;/span&gt;&lt;br /&gt;&lt;/p&gt;&lt;span style="font-family: Verdana; "&gt;1&lt;/span&gt;&lt;span style="font-family: Verdana; "&gt;.乔布斯常常引用他的偶像亨利.福特的一句名言：&amp;#8220;如果我问从来没见过汽车的顾客他们想要什么，他们肯定会告诉我，'一匹更快的马'&amp;#8221;。&lt;/span&gt;&lt;br /&gt;&lt;p&gt;&lt;span style="font-family: Verdana; "&gt;2.互联网公司某产品经理提了个idea：&amp;#8220;我们搞个微型博客，限制用户发文的字数不超过140个，可以关注别人&amp;#8221;。众人一片质疑：&amp;#8220;限制字数？这不开历史倒车吗？要关注功能直接在博客上加不就行了？&amp;#8221;大家说得都有道理，只是老板很支持产品经理试试看，没想到微博推出火得一塌糊涂。&lt;/span&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family: Verdana; "&gt;3.A和B是公司的两个很不错的程序员，A更擅长底层开发，B更擅长高层架构设计，一个项目中老板决定让A、B搭档形成优势互补。没想到结果A、B实际配合才发现两人思路差异太大， 不仅没有形成互补反而还互斥。&lt;/span&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family: Verdana; "&gt;通过上面的几个例子我们可以对不确定性现象有一个感性的认识，即我们很难在一个事物出现或事件发生之前通过理性思辨的方式推导出它出现后的效果。虽然事后我们常常可以总结个一二三，某人为什么成功，某产品为什么受到大家喜爱，某地区房价为什么会暴涨，某潮流为什么符合大趋势，但你能保证这些理由是充分的吗？你能在事前分析并预见到这些结果吗？这说明在分析和预测某些事情的时候光靠理性思辨常常是不可靠的，就像我们软件开发中，每个项目都会定大大小小的计划，但是常常是计划不如变化快，导致程序员常常抱怨需求变化太快而疲于应付。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family: Verdana; "&gt;为了更好的认识软件需求的不确定性，我们先来看软件需求有哪些影响因素？我认为&lt;/span&gt;&lt;span style="font-family: Verdana; " id="quote_66167"&gt;用户需求和外部环境是影响软件需求的两大因素。其中，用户需求是指用户所期望的结果，它从目标的角度影响软件需求；外部环境是指软件所运行和依赖的外部环境，它从实现基础的角度影响软件需求。而实际开发中我们面临两大问题&lt;/span&gt;&lt;span style="font-family: Verdana; " id="quote_66167"&gt;&lt;strong&gt;：1.用户需求在实际运行前的不确定性；2.外部环境在实际运行前的不确定性&lt;/strong&gt;。&lt;/span&gt;&lt;/p&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family: Verdana; " id="quote_66167"&gt;用户需求的不确定性是指&lt;/span&gt;&lt;span style="font-family: Verdana; " id="quote_66167"&gt;&lt;strong&gt;&lt;strong&gt;&amp;#8220;需求无法在用户真正能运行看到效果之前明确下来&amp;#8221;&lt;/strong&gt;&lt;strong&gt;，&lt;/strong&gt;&lt;/strong&gt;比如：让你开发一套Wow这样大型的游戏，你能想象游戏的效果是设计者一开始就想好了精确到每一个细节吗？&lt;/span&gt;&lt;span style="font-family: Verdana; " id="quote_66167"&gt;实际上，游戏的设计者通常只能借助文档，草图，Use  Case等非精确的方式大致提出需求，在看到效果之后才能逐步地细化和明确。所以，实际的开发过程一定是&amp;#8220;需求-&amp;gt;实现-&amp;gt;反馈-&amp;gt;需求-&amp;gt;...&amp;#8221;这样在需求和实现相互反馈的过程中不断进行的，那种希望做出完美的需求分析，让需求稳定以后再来实现的想法往往是不现实的。另外，还有一种极端的情况是根本不存在精确的用户需求，比如：自动化翻译软件，你能在实现之前就把翻译效果精确的描述出来吗？存在绝对正确的翻译方法吗？类似的还有淘宝推荐，它会猜测你喜欢的商品并推荐给你，这类功能存在绝对精确的推荐吗？&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family: Verdana; " id="quote_66167"&gt;外部环境的不确定性是指&lt;/span&gt;&lt;span style="font-family: Verdana; " id="quote_66167"&gt;&lt;strong&gt;"当我们的系统需要和外部系统集成时&lt;/strong&gt;&lt;/span&gt;&lt;span style="font-family: Verdana; " id="quote_66167"&gt;&lt;strong&gt;，关于外部系统行为的假设也无法在实际集成运行前完全确定"&lt;/strong&gt;， 比如：你做一套股票客户端系统需要连上交易所系统，尽管通常交易所会有相应的协议，但实际开发过程中，协议会有很多定义不清晰或未定义，甚至有实际行为与协议不符合的地方，这样我们只有先做假设实现，然后在交易所提供的测试环境中去确定。对于像交易所这样非常重视质量的环境都存在协议定义不清或者不全必须在集成测试时才能弄清楚的情况，对于普通的外部系统更是可想而知。所以，我们的开发常常只能先基于一定的假设，但是这些假设随时可能在实际集成的时候被修改。在实际体验外部环境之前，关于它的各种描述都是纸上谈兵，就像你拿着地图去了解一个陌生的地方一样，你了解了地图并不等于你就真的了解了那个地方。&lt;/span&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;由于这两种不确定性的存在，我们的软件开发必然也处于难以完全遵循计划的境地，多数试图在实现之前做出完美的需求分析和设计然后按部就班遵循计划的&lt;/span&gt;&lt;span&gt;企图都会以失败告终。&lt;/span&gt;&lt;span&gt;实际上，多数软件公司都意识到了需求频繁变动的现象，但是有的公司采取的措施却错得离谱，比如：某国内著名的公司为了应对需求的频繁变动专门成立了&amp;#8220;需求评审委员会&amp;#8221;，强调加强需求评审，要改变需求必须经委员会批准。这类做法实际上是没有认识到需求不确定性是一种自然的动力，试图通过人为调控改变自然的规律。&lt;/span&gt;&lt;span&gt;我们应该承认两种不确定性的普遍性，我们并把它作为制定开发策略的&amp;#8220;一等对象&amp;#8221;来对待，化被动为主动。&lt;/span&gt; &lt;br /&gt;&lt;/p&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/weidagang2046/aggbug/1967106.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/weidagang2046/archive/2011/03/01/1967106.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/weidagang2046/archive/2011/02/23/tdd-beautiful-or-not.html</id><title type="text">TDD到底美不美？</title><summary type="text">最近CoolShell上的一篇《TDD并不是看上去的那么美》引起了敏捷社区的高度关注和激励辩论。今天，InfoQ甚至专门举行了一个“虚拟座谈会”《TDD有多美？》，几位国内敏捷社区的名人专门就此问题展开了深入地讨论。不论结果如何，这种探讨和反思的精神还是非常值得赞赏的。事件实际上可以简单地归纳为“一个有一定影响力的开发人员质疑TDD，一群敏捷社区名人对TDD进行解释和辩护”。现在，就让我坚定地站在CoolShell一边，为对TDD的质疑和批判添砖加瓦吧！我们首先来看看TDD的核心理念是什...</summary><published>2011-02-23T14:42:00Z</published><updated>2011-02-23T14:42:00Z</updated><author><name>Todd Wei</name><uri>http://www.cnblogs.com/weidagang2046/</uri></author><link rel="alternate" href="http://www.cnblogs.com/weidagang2046/archive/2011/02/23/tdd-beautiful-or-not.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/weidagang2046/archive/2011/02/23/tdd-beautiful-or-not.html"/><content type="html">&lt;div&gt;&lt;strong style="font-size: 10pt;"&gt;&lt;/strong&gt; 	                 				 							 			&lt;div id="body_66167"&gt; 			&lt;p&gt;&lt;span id="quote_66167"&gt; 					&lt;/span&gt; 			&lt;/p&gt;&lt;span style="font-family: 'Lucida Console'; "&gt;最近CoolShell上的一篇&lt;/span&gt;&lt;a title="TDD并不是看上去的那么美" href="http://coolshell.cn/articles/3649.html"&gt;&lt;span style="font-family: 'Lucida Console'; "&gt;《TDD并不是看上去的那么美》&lt;/span&gt;&lt;/a&gt;&lt;span style="font-family: 'Lucida Console'; "&gt;引起了敏捷社区的高度关注和激励辩论。今天，InfoQ甚至专门举行了一个&amp;#8220;虚拟座谈会&amp;#8221;&lt;/span&gt;&lt;a title="《TDD有多美》" href="http://www.infoq.com/cn/articles/virtual-panel-tdd"&gt;&lt;span style="font-family: 'Lucida Console'; "&gt;《TDD有多美？》&lt;/span&gt;&lt;/a&gt;&lt;span style="font-family: 'Lucida Console'; "&gt;，几位国内敏捷社区的名人专门就此问题展开了深入地讨论。不论结果如何，这种探讨和反思的精神还是非常值得赞赏的。事件实际上可以简单地归纳为&amp;#8220;一个有一定影响力的开发人员质疑TDD，一群敏捷社区名人对TDD进行解释和辩护&amp;#8221;。现在，就让我坚定地站在CoolShell一边，为对TDD的质疑和批判添砖加瓦吧！&lt;/span&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family: 'Lucida Console'; "&gt;我们首先来看看TDD的核心理念是什么。第一是&amp;#8220;用例即规范&amp;#8221;&lt;span  style="font-family: verdana, 'courier new'; "&gt;&lt;span style="font-family: 'Lucida Console'; "&gt;（Specification by Example），即把测试用例作为需求规范的一种形式。传统的需求表达方式包括文档，Use Case等，而TDD强调通过测试用例来表达需求。另外，TDD的测试用例是黑盒的基于外部接口的，所以，它实际上又是对外部接口的设计。&amp;#8220;不把测试用例单纯地视为测试，而从需求和设计的角度来看测试用例&amp;#8221;&lt;/span&gt;&lt;span style="font-family: 'Lucida Console'; "&gt;是TDD与传统测试的一个重要区别。TDD的第二个重要理念是Test First，强调测试对于实现的驱动作用，先写测试用例，再实现和重构。Test First的实质是&amp;#8220;先理解清楚需求，并做好外部接口设计，把它转化为测试用例，然后再来实现和重构&amp;#8221;。&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;div&gt;&lt;span style="font-family: 'Lucida Console'; "&gt;如果说&amp;#8220;用例即规范&amp;#8221;还弥补了文档和Use Case在表达需求时的某些不足，具有一定的好处，那么&lt;/span&gt;&lt;strong style="font-family: 'Lucida Console'; "&gt;Test First则有很大的问题&lt;/strong&gt;，尤其&amp;#8220;在没有测试用例失败之前，不要写任何一行代码&amp;#8221;的极端方式则更是极端的错误。&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;div&gt;&lt;span id="quote_66167" style="font-family: 'Lucida Console'; "&gt;如果测试用例就是需求和设计，那么为什么不能先写出测试用例再来实现呢？这不是我们最熟悉的先需求再设计再编码吗？答案是：&lt;/span&gt;&lt;span id="quote_66167" style="font-family: 'Lucida Console'; "&gt;不能执行的测试用例（Test First）和能执行的测试用例有着天壤之别，&lt;/span&gt;&lt;span id="quote_66167" style="font-family: 'Lucida Console'; "&gt;&lt;strong&gt;你写出了测试用例不代表你就看到了运行的实际效果&lt;/strong&gt;。不能执行的测试用例和写在纸上的文档相比对实现的指导意义不见得能好到哪里去！除非是一些很简单的情况下，在实际的软件开发中，你很难在没有执行测试用例的情况下写出真正符合最终需求的测试用例来。比如：你做一个页面，页面的效果需求和设计通常会在真正可以运行之后不断调整，在实现之前只能有一个大致的轮廓和方向，许多方面的细节要么是没想清楚，要么是完全没想到，不可能一蹴而就。如果片面强调测试对实现的驱动作用，那么实际上隐含了&amp;#8220;需求和设计的细节可以在实现之前明确下来&amp;#8221;的假设，这是非常不敏捷的和不现实的！&lt;/span&gt;&lt;span style="font-size: 10pt;" id="quote_66167"&gt;&lt;/span&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span id="quote_66167" style="font-family: 'Lucida Console'; "&gt;Test First要求写测试用例时对软件需求有精确的了解，但实际软件开发过程中用户需求和外部环境的不确定性会导致软件需求难以把握和频繁变动。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span id="quote_66167" style="font-family: 'Lucida Console'; "&gt;用户需求的不确定性是指&lt;/span&gt;&lt;span id="quote_66167" style="font-family: 'Lucida Console'; "&gt;&lt;strong&gt;&amp;#8220;需求无法在用户真正能运行看到效果之前明确下来&amp;#8221;&lt;/strong&gt;&lt;strong&gt;。&lt;/strong&gt;比如：让你开发一套Wow这样大型的游戏，你能想象游戏的效果是设计者一开始就想好了精确到每一个细节吗？对于游戏这样的软件，需求和设计不可能脱离实际运行纸上谈兵地产生。&lt;/span&gt;&lt;span id="quote_66167" style="font-family: 'Lucida Console'; "&gt;游戏的设计者通常只能借助文档、草图、Use Case等非精确的方式大致提出需求，先做出原型，在看到效果之后才能逐步地细化和明确，需求设计的增加和改变会伴随整个软件开发过程。另外，还有一种极端的情况是根本不存在精确的用户需求，比如：自动化翻译软件，你能在实现之前就把翻译效果用测试用例固定下来吗？存在绝对正确的翻译方法吗？最近，我们和国外一家大公司客户谈一个项目需求的时候，客户讲了这样一句话&amp;#8220;我们现阶段还无法提出很细致的需求，只有等你们拿出第一个版本，然后我们再逐步地调整细化&amp;#8221;。我们的客户没有宣称自己在做敏捷，但人家的思维方式多敏捷啊，不是什么一上来就明确需求，而且还要精确写出自动化测试用例。有人说这种情况我们仍然可以先根据自己的理解进行TDD，这样做可以：1.基于测试用例和客户沟通明确需求；2.驱动实现。我对此持不同看法，能执行的测试用例和不能执行的测试用例有着天壤之别，客户从测试用例根本无法获得真实运行的体验，你能想象苹果把iPhone的测试用例写在PPT上，给用户做一个演讲，用户就能给出关于iPhone设计的反馈了吗？要真正的用户反馈，就需要实打实的软件，这不正是敏捷的&amp;#8220;Working software over document&amp;#8221;的思想吗？另外，既然用户无法在实际体验之前提出反馈，那么开发人员在开发初期做的需求分析和设计都只是一个探索，随时可能调整甚至被推翻，不值得在实现之前进行自动化测试设计的投资。&lt;/span&gt;&lt;span style="font-size: 10pt;" id="quote_66167"&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span id="quote_66167" style="font-family: 'Lucida Console'; "&gt;外部环境的不确定性是指&lt;/span&gt;&lt;span id="quote_66167" style="font-family: 'Lucida Console'; "&gt;&lt;strong&gt;"当我们的系统需要和外部系统集成时&lt;/strong&gt;&lt;/span&gt;&lt;span id="quote_66167" style="font-family: 'Lucida Console'; "&gt;&lt;strong&gt;，关于外部系统行为的假设也无法在实际集成运行前完全确定"&lt;/strong&gt;。例如，要做一套&lt;span  style="font-family: verdana, 'courier new'; "&gt;&lt;span id="quote_66167" style="font-family: 'Lucida Console'; "&gt;股票客户端连上交易所系统&lt;/span&gt;&lt;/span&gt;&lt;span  style="font-family: verdana, 'courier new'; "&gt;&lt;span id="quote_66167" style="font-family: 'Lucida Console'; "&gt;，因为交易所的行为会直接影响到客户端的开发，所以只有在弄清交易所行为的情况下才谈得上开发出高质量的客户端。如果采用测试驱动，编写了各种涉及交易所行为的测试用例，比如什么情况下发什么类型的消息，消息格式如何，如何交互等等，但是这些测试用例本身是否正确却需要打一个大大的问号！这一方面是由于很多交易所提供的协议都不够清晰或者有许多未明确定义的地方；另一方面即使协议没有问题，开发人员也可能由于单纯的失误或者缺乏相应领域的基本知识而把协议理解错。实际上，要真正弄清交易所的行为明确客户端的需求，最重要的手段还是在交易所提供的测试环境中跑集成测试&lt;/span&gt;&lt;/span&gt;&lt;span  style="font-family: verdana, 'courier new'; "&gt;&lt;span id="quote_66167" style="font-family: 'Lucida Console'; "&gt;。对于Test First来讲，测试用例本身的错误可以说是代价最大的，不仅浪费时间和精力，更重要的是还打击开发人员的士气，谁愿意来回折腾呢？但很不幸，实际情况是&lt;/span&gt;&lt;span  style="font-family: 'Lucida Console'; "&gt;在最初没有明确交易所行为的时候Test First出来的测试用例随时可能在真实集成后被推翻，并且如果是比较高层的需求分析失误，那对整个架构设计来讲会是灾难性的后果。在实际开发中，我们的软件需要和其他系统集成的情况是非常普遍的，而期望在没有进行实际集成的情况下弄清外部系统的行为都是不现实和不敏捷的。&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span id="quote_66167" style="font-family: 'Lucida Console'; "&gt;所以，&lt;/span&gt;&lt;span id="quote_66167" style="font-family: 'Lucida Console'; "&gt;&lt;strong&gt;Test First需要对于被测系统的需求和环境有精确的了解，但由于需求不确定性和外部环境不确定性两大问题，Test　First在很多时候都是不现实的&lt;/strong&gt;。其实，Test &#xD;
First和瀑布式思想一脉相承，都强调需求先于实现，而忽略了软件需求的产生会受到实现的反馈，会在实际运行中不断调整探索完善。TDD无非是把需求分析的结果用测试用例表达，替代传统用文档表达需求，但从宏观上看，TDD和瀑布比是换汤不换药，这都不是真正的敏捷。除了简单情况，不存在脱离实现的需求，你能够在明确了需求之后就实现出一套Linux系统吗？既然你根本无法实现一套Linux系统，那么这样所谓的需求又有多大的意义呢？所以，能提出什么样的需求不能脱离你的实现能力。&lt;strong&gt;需求和实现之间不是简单的谁驱动谁，而是一种相互反馈的关系&lt;/strong&gt;，这与需求用什么方式表达没有关系。正如瀑布模型无法在初始阶段做出完美的需求分析，TDD也无法在初始阶段做出完美的测试用例；不仅如此，自动化测试用例的开发维护成本还远高于文档。所以，在敏捷环境中，软件开发初期应该通过文档和用例等手段大致表达需求，实现之后在实际运行中体验效果，不断优化探索和明确需求和外部环境，当需求和对外部环境的认识达到一个比较稳定的程度才编写测试用例将需求固化下来。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;div&gt;&lt;span id="quote_66167" style="font-family: 'Lucida Console'; "&gt;上面的论述主要针对贴近最终用户的外部需求（如ATDD），下面我会进一步解释即使是在内部的单元测试级别TDD仍然有问题。我们还是首先从需求入手，思考一下单元的需求是哪里来的呢？答案是：需求来自于设计！比如，对轮胎的需求来源于汽车的设计，低层模块的需求来源于高层模块的设计。而在开发初期，这种内部设计具有很大的不稳定性，带有很多假设的成分，在没有进行集成测试的情况下，很难讲这种内部设计是否合理。实际项目开发通常会在集成运行之后不断调整内部的设计，即影响单元的需求。那么，如果是测试驱动，首先按不成熟的内部设计把一个个单元需求编写成单元测试再来实现，实际上大大推迟了能进行集成测试的时间，对于真正快速弄清高层需求稳定设计反而是不利的。假设最终还是所有单元都完成，然后开始运行集成或验收测试，这时候有两种可能：1.用户看到实际效果，决定调整需求；2.发现集成前在单元层面的假设不成立或者是有没有考虑到的情况。不论是哪一种情况发生，以前所写的单元测试都面临着被废弃或必须修改的命运。实际上，多数与业务相关的单元测试用例比起集成或验收测试用例更加不稳定，因为它会受到所有其上层模块的需求和设计变动的影响。由于我们在不稳定的单元测试上浪费了大量的时间（按我的经验编写单元测试比编写实现更耗时），这就导致了迟迟无法进行集成看到实际效果，也没有办法敏捷地应对需求的调整。也就是说具有讽刺意味的，&lt;/span&gt;&lt;span id="quote_66167" style="font-family: 'Lucida Console'; "&gt;&lt;strong&gt;Test First理念居然是和敏捷理念矛盾的！&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;所以，我认为Test   First不符合敏捷开发的基本假设，而真正符合敏捷的理念是&amp;#8220;需求和设计依赖于实现的反馈，需要在实际运行过程中根据效果不断探索调整得来的，不可能脱离实际运行写出真正符合最终需求的测试用例来&amp;#8221;。所以，&lt;strong&gt;我们真正应该做的是尽快看到实际运行的效果&lt;/strong&gt;，而自动化测试作为固化的需求和设计是在看到效果之后。在集成之前花太多精力进行测试驱动只会导致迟迟看不到实际运行效果（尤其是基于开发人员自己的假设编写大量单元测试用例），看到效果需要调整需求又会废掉或改掉一大堆的测试用例。实际上，越是外部的需求其变更带来的影响和代价越大，越是需要尽早明确。从宏观上看，&lt;strong&gt;TDD所谓的快速反馈实际上是加快内部反馈，延迟了外部反馈，这无异于本末倒置&lt;/strong&gt;。而大量需要修改或作废的测试用例其实是一种很大的浪费，这和消除浪费的精益思想也是矛盾的！&lt;/span&gt;&lt;/div&gt;&lt;p&gt;&lt;span style="font-size: 10pt;" id="quote_66167"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;div&gt;&lt;span id="quote_66167"&gt;&lt;div align="center"&gt;&lt;img alt="" src="http://images.cnblogs.com/cnblogs_com/weidagang2046/feedback_cycle.jpg" border="0" height="436" width="564" /&gt;&lt;/div&gt;&lt;/span&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 10pt;" id="quote_66167"&gt;&lt;/span&gt;&lt;span id="quote_66167" style="font-family: 'Lucida Console'; "&gt;上面这幅cost/length_of_feedback_cycle图是我们常见的用于说明敏捷方法比传统方法具有更短的反馈周期，更小代价的应对变化。从图中我们可以清晰的看到在验收测试中发现的需求错误导致的代价是最高的。如果验收测试往后推迟一点，发现错误的代价将按非线性地增长。上面我们已经论述了，任何方法都不可能消除验收测试后对需求的调整，因为这是需求产生的正常过程。我们唯一可以做的是尽可能地缩短验收测试的反馈周期，但是很不幸TDD大量的内部测试只会导致推迟验收测试的时间，从而大大增加代价。&lt;/span&gt;&lt;span id="quote_66167" style="font-family: 'Lucida Console'; "&gt;在实际开发中，我提倡&lt;/span&gt;&lt;span id="quote_66167" style="font-family: 'Lucida Console'; "&gt;&lt;strong&gt;在第一次集成运行测试之前不要写单元测试用例&lt;/strong&gt;；自动化的验收测试用例则视编写和维护的代价而定，如果代价比较高，则应该采用文档和Use  &#xD;
Case来描述需求，因为这两种方式比自动化的验收测试更容易维护。编写单元测试一定是在集成以后，这样才能首先得到外部反馈，尽量&lt;strong&gt;先保证做正确的事情，再正确地做事&lt;/strong&gt;。&lt;/span&gt;&lt;span style="font-size: 10pt;" id="quote_66167"&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;span style="font-family: 'Lucida Console'; "&gt;下面这段话来自于InfoQ文章&lt;/span&gt;&lt;a title="《Mock不是测试的银弹》" href="http://www.infoq.com/cn/articles/thoughtworks-practice-partvi"&gt;&lt;span style="font-family: 'Lucida Console'; "&gt;《Mock不是测试的银弹》&lt;/span&gt;&lt;/a&gt;&lt;span style="font-family: 'Lucida Console'; "&gt;：&amp;#8220;在使用JMock框架后测试编写起来更容易，运行速度更快，也更稳定，然而出乎意料的是产品质量并没有如我们所预期的随着不断添加  的测试而变得愈加健壮，虽然产品代码的单元测试覆盖率超过了80%，然而在发布前进行全面测试时，常常发现严重的功能缺陷而不得不一轮轮的修复缺陷、回归  测试。为什么编写了大量的测试还会频繁出现这些问题呢？ &amp;#8221;这描述的情况和我在实践中遇到的情况类似，不过很可惜文章并没有找到问题真正的原因。真正的原因不是什么Mock不Mock，而是TDD的单元测试是基于开发人员的假设，这些假设的测试即使全部通过代码覆盖率100%，到了集成测试发现假设根本不成立或者原先在单元层面很多情况没有考虑到，这又怎能保证高质量？在TDD的实践者中我见到过不少类似这样的，他们很认真，编写了很多单元测试用例，代码覆盖率也很高，但他们其实是有意无意在&lt;/span&gt;&lt;span style="font-family: 'Lucida Console'; "&gt;&lt;strong&gt;先正确地做事（单元测试），再做正确的事（集成测试），这就是本末倒置。&lt;/strong&gt;&lt;/span&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;/div&gt;&lt;p&gt;&lt;span id="quote_66167" style="font-family: 'Lucida Console'; "&gt;当然，我不是全盘否定TDD。&lt;/span&gt;&lt;span id="quote_66167" style="font-family: 'Lucida Console'; "&gt;&lt;strong&gt;TDD在某些需求比较固定的场合是适用的，尤其是与具体业务关系不大的需求&lt;/strong&gt;，比如：写一个通用的数据结构，实现一个通用算法。TDD的先关注需求和思考外部接口设计的理念也对促进开发人员的抽象思维有很大益处。另外，TDD通常也具有较高的代码覆盖率。本文的主要观点在于：实际项目中，由于用户需求不确定性和外部环境不确定性，不要期望可以在实现之前完全明确需求，需求是在实际运行看到效果之后才逐步明确的；我们的开发过程必须能够敏捷地适应需求的变化，而TDD的Test First理念恰好与之矛盾。所以，对于TDD不了解的朋友，我建议应该学习和实践TDD，从而获得其益处；同时我也提醒TDD存在理论上的缺陷，这是在实践中需要特别留意的。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;/div&gt;&lt;img src="http://www.cnblogs.com/weidagang2046/aggbug/1963277.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/weidagang2046/archive/2011/02/23/tdd-beautiful-or-not.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/weidagang2046/archive/2011/02/20/1956306.html</id><title type="text">聊聊结对编程</title><summary type="text">在敏捷软件开发的各种实践中，结对编程（Pair Programming，下文简称Pair）是特别有争议的。Pair有一个特点，那就是还没有进行过任何Pair实践前，你很可能对它已经有了&amp;#8220;喜欢&amp;#8221;或者是&amp;#8220;讨厌&amp;#8221;的印象。如果有人问你，你喜欢持续集成吗？你多半会回答：不是很肯定，需要试试看。但如果有人问你，你喜欢Pair吗？我猜你会马上给予明确的肯定或否定的回答。喜欢它的人会觉得好处多多而成本低低，不喜欢它的人会觉得讨厌得难以想象。喜欢与不喜欢都可以形成强大的阵营，两边都不乏重量级的高手。Pair的优点说起来都很明显，比如：快速反馈，更好的设计，甚至更高</summary><published>2011-02-20T09:31:00Z</published><updated>2011-02-20T09:31:00Z</updated><author><name>Todd Wei</name><uri>http://www.cnblogs.com/weidagang2046/</uri></author><link rel="alternate" href="http://www.cnblogs.com/weidagang2046/archive/2011/02/20/1956306.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/weidagang2046/archive/2011/02/20/1956306.html"/><content type="html">&lt;p&gt;在敏捷软件开发的各种实践中，结对编程（Pair Programming，下文简称Pair）是特别有争议的。Pair有一个特点，那就是还没有进行过任何Pair实践前，你很可能对它已经有了&amp;#8220;喜欢&amp;#8221;或者是&amp;#8220;讨厌&amp;#8221;的印象。如果有人问你，你喜欢持续集成吗？你多半会回答：不是很肯定，需要试试看。但如果有人问你，你喜欢Pair吗？我猜你会马上给予明确的肯定或否定的回答。喜欢它的人会觉得好处多多而成本低低，不喜欢它的人会觉得讨厌得难以想象。喜欢与不喜欢都可以形成强大的阵营，两边都不乏重量级的高手。Pair的优点说起来都很明显，比如：快速反馈，更好的设计，甚至更高的效率等等。但据我了解，不喜欢Pair的人基本上都承认Pair有优点，只是他们通常会提出反对意见，比如：Pair要求两个人协调一致，每个人的作息时间需要同步，如果一个人请假，那另一个人怎么办？等待就太浪费时间了，如果自己一个人做那就不是Pair了；或者也有很多人感觉到Pair的时候特别累，不像一个人工作那样自主，写一小时程序，上十分钟微博，两个人同一台电脑工作自由就小多了。 &lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;我所在的公司是Pair的实践者，对于喜欢Pair或者不喜欢Pair的各种原因和心态我都有切身的体会。在实践中，我们并没有完全一成不变地照搬Kent Beck在《Extreme Programming Explained》书中所定义的Pair模式（Driver Observer模式）。因为，Pair毕竟是一种实践方法，可以供我们在软件开发中参考，而不必像对待科学理论一样教条。另外，我认为要让这种实践方法发挥它的威力，消除人们心中的讨厌甚至是恐惧情绪是首先要解决的问题，甚至我们可以为此修改一些Pair的传统做法。下面我先来大致描述一下我们的实践模式（&lt;strong&gt;Owner Supporter模式&lt;/strong&gt;）：&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&amp;lt;阶段1-Pair形成&amp;gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Team在接到一个大的Story后经过讨论并分解任务形成一个个粒度适中的Task。Task的粒度一般在几小时到一两天这种级别，涉及的代码规模大约是几行到几个类/函数。每一个Task，都有一个Owner作为责任人，这是和传统单兵开发模式相同的。不过，Owner的产生一般不是由ScrumMaster来分配指定，而是Team成员自发地去协商选择适当的任务。Owner拿到Task以后会从Team中找一个Supporter，形成Pair。这也是一个自发协商过程，没有强制指定，也没有固定的搭档，这样形成的Pair一般会是配合最默契也最适合相应Task的。&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&amp;lt;阶段2-初始讨论&amp;gt;&lt;strong&gt;&lt;br /&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Pair形成以后， 一般是由Owner先分析需求，设计测试，并初步分析思考得出初步的设计思路。这时，Supporter可以暂时先不了解需求。然后Owner邀请Supporter一起，向Supporter介绍需求和自己的设计思路。Supporter这时需要帮助Owner理解需求，设计测试，对设计方案提出反馈意见，或者提出新的设计。讨论的理想结果是两人达成一致形成最终的设计方案，如果两人分歧严重不能达成一致则需要更多的人参与，比如：可以举行一个小型的技术讨论会议把讨论扩大到整个Team。&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;div&gt;&lt;p&gt;&amp;lt;阶段3-具体实现&amp;gt;&lt;strong&gt;&lt;br /&gt; &lt;/strong&gt;&lt;/p&gt;这一阶段是根据讨论得出的设计方案开始编程实现。在经典Pair Programming中，两个人是在同一台电脑前一起工作，并不时地交换；但我们的实践发现这一点是特别容易让人疲倦，感觉不自在，从而抵触Pair的最大因素。所以，我们在实践过程中，除了少数情况，基本上没有采用全程Pair工作，而是由Owner一个人完成。当然，如果Owner遇到了一些设计中考虑不完善的地方，他可以随时再和Supporter讨论。&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&amp;lt;阶段4-Review&amp;gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;具体实现完成以后，Owner又会邀请Supporter来review自己的工作。这个过程一般是借助版本控制工具对比代码改动，Supporter从实现的角度来审查实现是否符合设计，有没有可以直接观察到的bug和潜在的问题。如果发现问题并讨论确认，Owner再做相应的改动，再Review。最终Supporter不能review出问题了，Owner提交代码。&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&amp;lt;阶段5-维护&amp;gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;虽然经过了讨论和review， 但在后续的测试或者上线后仍然会陆续发现问题，这时就涉及到维护工作。维护工作依然是由Owner牵头，分析log，看代码，重现，修正；但是，Owner也可以随时邀请Supporter参与分析讨论，至少在Owner准备修改原先的代码时应该要告诉Supporter&amp;#8220;原先我们某个地方没有考虑周全，有什么什么问题，我打算怎么怎么改动&amp;#8221;。这个过程好像又回到了阶段2，然后阶段3，阶段4 ... 开发中还有一种可能是，原先的Supporter或Owner已经不在Team内了，这时就需要加入一个新人形成Pair，有经验的人应该向自己的新搭档介绍Task的相关背景。当然，还很有可能碰到根本没有熟悉该模块的人了，这时就必须形成一个新的Pair，把维护作为一项新Task。&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;上面基本上就是我们的Pair实践模式了。这种模式基本上还是保留了大部分经典Pair模式的要点，主要的区别在于：&lt;/p&gt;&lt;p&gt;1. 把Pair的形成纳入Scrum框架的任务划分过程。Scrum通常会在Sprint的计划会议中将大的User Story拆分成许多小的Task，每个Task一般对应几小时到一两天的工作量。Task列表出来以后，Team自发协商每个Task形成一个Pair，整个过程一般没有管理者的直接干预。Story的背景需要Team中的每个人都了解，Task的细节由Pair负责。Pair的效果通常也依赖于Task的合理划分。&lt;br /&gt;2. 避免全程Pair。因为，全程Pair在实践和心理上会遇到很多问题，比如：时间同步问题，并行工作问题，不自在感问题。避免全程Pair可以使得Owner和Supporter的时间安排更加灵活，增加并行性，减少不自在感。大部分不喜欢Pair的人基本上都是尤其不喜欢全程Pair，所以这项改动对于吸引更多的人采用Pair具有重要的意义。&lt;br /&gt;&lt;/p&gt;&lt;p&gt;3. 区分Owner和Supporter的职责。经典Pair模式中Driver和Observer角色是随时交换的，也就是说对于一个Task来讲两个人的职责是相同的，这样可以减小每一个人的压力，但又容易造成缺乏责任感。Owner Supporter模式是单兵模式和经典Pair模式之间的一种妥协，试图在责任和压力之间找到一种平衡。Owner是驱动整个过程的主导，并且是主要的具体实现者；Supporter以辅助为主，在讨论和review的时候可以站在不同的角度帮助Owner。&lt;strong&gt;Supporter并非没有责任，熟悉Task的所有细节是Supporter的责任。&lt;/strong&gt;当Owner由于请假或忙于其他事物时，Supporter应该立即可以代替Owner处理Task的实现或维护。如果Supporter做不到完全的backup，这就是Supporter的失职，在整个Team和领导的心中应该有这样的认识。&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;最后，除了显而易见的&amp;#8220;引入反馈，提高质量&amp;#8221;等很&amp;#8220;官方&amp;#8221;的优点外，我从管理者和开发人员的角度分别总结一下Owner Supporter模式的&amp;#8220;不为人知&amp;#8221;却真正很有价值的优点。&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;管理者角度：&lt;br /&gt;&lt;/p&gt;&lt;p&gt;1. 应对人员变动：每个模块至少两个人熟悉，这样在有人请假的时候都有backup，不会造成严重的耽误工作进度。如果有人要离职，交接工作通常也会非常轻松，几乎没有什么需要特别交接的。很多项目都缺乏文档，只有实现的人知道怎么回事，而即使有文档也总有不清楚的地方，所以留住熟悉的人就是留下了活文档。&lt;/p&gt;&lt;p&gt;2. 增加对质量的信心：相比单兵作战模式，即使是不懂技术的管理者，Pair也可以自然地增加他们对于软件质量的信心，毕竟&amp;#8220;三个臭皮匠顶个诸葛亮&amp;#8221;。另外，需要特别提到的是安全性问题。我曾经听说过一个电信计费项目，一个开发人员故意留了一个漏洞，把钱自动打入自己的银行卡，最终为公司带来了麻烦。如果采用Pair方式，这种情况就不那么容易发生。&lt;br /&gt;&lt;/p&gt;&lt;p&gt;3. 过程透明，增加反馈：信息反馈是敏捷方法的重要支柱，而及时准确的反馈又依赖于过程的透明。在Pair模式下，整个软件开发过程是透明的，开发人员不容易有意或无意的隐藏项目的细节和风险，因而管理者更容易获得及时准确的反馈。另外，对于同一个事物，不同的人也会有不同的看法，这是很正常的现象。比如，在对问题进行估计时一个人乐观，一个人悲观，这时管理层可以同时参考他们的意见，对问题掌握得更加全面。&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;4. 代替培训：相比传统课程或讲座式培训，Pair能让新员工更快地适应项目。Pair方法也和一些公司采用的导师制有明显的区别。导师制更强调师傅带徒弟，而Pair则不然。在实践中，我们更鼓励基础比较好的新员工多做Owner，老员工作为Supporter支持；只对基础比较差的新员工先从Supporter做起，然后逐步过渡到Owner。这样做是因为新员工一般积极性比较高，适合承担更多的具体工作，而老员工则应该避免重复劳动，而是以辅助和指导为主。另外，新员工也可能带来一些新的思想、方法和技能，这些反而是老员工应该学习的。&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;开发人员角度：&lt;/p&gt;&lt;p&gt;1. 分担压力，增加默契：Task依然具有明确的责任人，但Supporter可以分担Owner的压力，即使是Team内的技术高手有一个人做backup也是有益的。有人讨论有人支持更容易达到良好的工作效果，而大家相互Support也可以让团队更加默契。&lt;br /&gt;&lt;/p&gt;&lt;p&gt;2. 消除经典Pair的不自在感： Owner Supporter模式与经典Pair的Driver Observer模式相比更少存在不自在感，因为整个过程是Owner主导的，他有更多的自由安排什么时候讨论，什么时候独立工作。&lt;br /&gt;&lt;/p&gt;&lt;p&gt;3. 分享知识和技能：在Pair的过程中Owner和Supporter会自然地相互传播和分享知识和技能，有助于学习提高。&lt;br /&gt;&lt;/p&gt;&lt;p&gt;4. 培养组织能力和表达能力： Owner相当于一个微型团队的leader，在Pair过程中可以锻炼组织能力和表达能力。这个过程可以让一些不善于交流和组织的技术人员逐渐改善交流和组织能力。 &lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;Owner Supporter模式在我们的实践中收到了良好的效果，而且成本很低，值得推荐！ &lt;br /&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/weidagang2046/aggbug/1956306.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/weidagang2046/archive/2011/02/20/1956306.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/weidagang2046/archive/2010/12/25/1916794.html</id><title type="text">boost::mutex vs boost::recursive_mutex</title><summary type="text">boost::mutex is not re-entrant, a thread can only lock it once, otherwise it’s dead-locked. If you need re-entrant mutex, the boost::recursive_mutex is the choice.</summary><published>2010-12-25T08:53:00Z</published><updated>2010-12-25T08:53:00Z</updated><author><name>Todd Wei</name><uri>http://www.cnblogs.com/weidagang2046/</uri></author><link rel="alternate" href="http://www.cnblogs.com/weidagang2046/archive/2010/12/25/1916794.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/weidagang2046/archive/2010/12/25/1916794.html"/><content type="html">&lt;p&gt;boost::mutex is not re-entrant, a thread can only lock it once, otherwise it&amp;#8217;s dead-locked. The following code snippet demonstrates it:&lt;/p&gt;  &lt;p&gt;&lt;font color="#8000ff"&gt;#include "boost/thread/mutex.hpp"&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#8000ff"&gt;#include &amp;lt;iostream&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#8000ff"&gt;boost::mutex mtx;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#8000ff"&gt;void bar(){&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#8000ff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; boost::mutex::scoped_lock lLock(mtx);&lt;/font&gt;&lt;strong&gt;&lt;font color="#ff0000"&gt;//!! dead-locked here&lt;/font&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#8000ff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; std::cout &amp;lt;&amp;lt; "bar" &amp;lt;&amp;lt; std::endl;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#8000ff"&gt;}&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#8000ff"&gt;void foo() {&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#8000ff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; boost::mutex::scoped_lock lLock(mtx);&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#8000ff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; std::cout &amp;lt;&amp;lt; "foo" &amp;lt;&amp;lt; std::endl;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#8000ff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; bar();&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#8000ff"&gt;}&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#8000ff"&gt;int _tmain(int argc, _TCHAR* argv[]){&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#8000ff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; foo();&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#8000ff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; return 0;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#8000ff"&gt;}&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;If you need re-entrant mutex, the boost::recursive_mutex is the choice. The following code snippet demonstrates it:&lt;/p&gt;  &lt;p&gt;&lt;font color="#8000ff"&gt;#include "boost/thread/recursive_mutex.hpp"&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#8000ff"&gt;#include &amp;lt;iostream&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#8000ff"&gt;boost::recursive_mutex r_mtx;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#8000ff"&gt;void bar(){&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#8000ff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; boost::recursive_mutex::scoped_lock lLock(r_mtx);&lt;/font&gt;&lt;font color="#008000"&gt;&lt;strong&gt;//no problem for a thread to lock more than once&lt;/strong&gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#8000ff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; std::cout &amp;lt;&amp;lt; "bar" &amp;lt;&amp;lt; std::endl;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#8000ff"&gt;}&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#8000ff"&gt;void foo() {&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#8000ff"&gt;boost::recursive_mutex::scoped_lock lLock(r_mtx);&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#8000ff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; std::cout &amp;lt;&amp;lt; "foo" &amp;lt;&amp;lt; std::endl;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#8000ff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; bar();&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#8000ff"&gt;}&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#8000ff"&gt;int _tmain(int argc, _TCHAR* argv[]){&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#8000ff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; foo();&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#8000ff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; return 0;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#8000ff"&gt;}&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;I also did a benchmark on my PC. For 1 locking operation, the result is approximately: boost::mutex: 0.043 micro second, boost::recursive_mutex: 0.068 micro second.&lt;/p&gt;  &lt;p&gt;Re-entrant mutex is the default in Java and C#. Generally speaking, if a mutex is shared by many modules/classes, it&amp;#8217;s recommended to use boost::recursive_mutex; while if it&amp;#8217;s only used by a single module/class and no re-entrant feature needed, it&amp;#8217;s recommended to use boost::mutex. &lt;/p&gt;&lt;img src="http://www.cnblogs.com/weidagang2046/aggbug/1916794.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/weidagang2046/archive/2010/12/25/1916794.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry></feed>
