<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

  <title><![CDATA[Yaodan's Blog]]></title>
  <link href="http://yaodanzhang.com/atom.xml" rel="self"/>
  <link href="http://yaodanzhang.com/"/>
  <updated>2015-06-29T12:10:40+08:00</updated>
  <id>http://yaodanzhang.com/</id>
  <author>
    <name><![CDATA[Yaodan Zhang (张耀丹)]]></name>
    
  </author>
  <generator uri="http://octopress.org/">Octopress</generator>

  
  <entry>
    <title type="html"><![CDATA[函数式编程的基石 - Y Combinator]]></title>
    <link href="http://yaodanzhang.com/blog/2015/06/11/functional-programming-fundamental-y-combinator/"/>
    <updated>2015-06-11T12:39:44+08:00</updated>
    <id>http://yaodanzhang.com/blog/2015/06/11/functional-programming-fundamental-y-combinator</id>
    <content type="html"><![CDATA[<p>本文主要目的是给大家介绍函数式编程中一个非常重要的知识：Y Combinator。包括它是什么，干什么用的，它为什么能够work，它的数学含义以及如何用重构的方式“推倒”他。</p>

<!--more -->


<p>（本文用到的示例代码会用JavaScript和Clojure两种语言来写。）</p>

<h2>它是什么</h2>

<p>Y Combinator也叫做不动点组合子（Fix Point Combinator），是由著名的逻辑学家Haskell Curry发明的（或者说是他发现的）。大家看到这个名字有没有很熟悉的感觉？你猜对了，Haskell语言就是为了纪念他而以他命名的。还有，函数式语言里面著名的柯里化（Currying）也是以他命名的。</p>

<p>我们可以用它来计算一个函数（lambda表达式）的不动点。不知道不动点是什么？翻译成中文来说就是它让我们拥有了在函数式语言里面写递归的能力。</p>

<p>那么问题来了，写个递归很简单嘛，为什么没有它我们就写不了呢？</p>

<h3>Lambda演算</h3>

<p>为了让大家更好的理解这个问题，我们还需要学习一点点lambda演算的知识。本来还想先写一篇文章介绍lambda的，后来发现了<a href="http://liujiacai.net/blog/2014/10/12/lambda-calculus-introduction/">这篇文章</a>。里面的内容很详尽，而且写得比较通俗，不熟悉lambda演算的话可以看看。</p>

<p>当然了，大家以编程语言来理解Y Combinator也可以，但是Church最初提出的lambda演算才是最简洁的，也最容易理解。</p>

<p>看完过后有一点请大家务必牢记在心，并跟我一起默念三遍：“lambda演算中的函数都是匿名函数”，“lambda演算中的函数都是匿名函数”，“lambda演算中的函数都是匿名函数”。</p>

<p>好了，我们步入正题。</p>

<h2>问题的提出</h2>

<p>递归函数，相信大家都会写，我们可以先写一个最简单的递归函数：计算阶乘，然后我们来看看有什么问题：</p>

<figure class='code'><figcaption><span>factorial.js</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="kd">var</span> <span class="nx">factorial</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">n</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>  <span class="k">return</span> <span class="nx">n</span> <span class="o">==</span> <span class="mi">1</span> <span class="o">?</span> <span class="mi">1</span> <span class="o">:</span> <span class="nx">n</span> <span class="o">*</span> <span class="nx">factorial</span><span class="p">(</span><span class="nx">n</span> <span class="o">-</span> <span class="mi">1</span><span class="p">);</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="nx">factorial</span><span class="p">(</span><span class="mi">5</span><span class="p">);</span> <span class="c1">// 120</span>
</span></code></pre></td></tr></table></div></figure>




<figure class='code'><figcaption><span>factorial.clj</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='clojure'><span class='line'><span class="p">(</span><span class="k">def </span><span class="nv">factorial</span>
</span><span class='line'>  <span class="p">(</span><span class="k">fn </span><span class="p">[</span><span class="nv">n</span><span class="p">]</span> <span class="p">(</span><span class="k">if </span><span class="p">(</span><span class="nb">= </span><span class="nv">n</span> <span class="mi">1</span><span class="p">)</span> <span class="mi">1</span>
</span><span class='line'>             <span class="p">(</span><span class="nb">* </span><span class="nv">n</span> <span class="p">(</span><span class="nf">factorial</span> <span class="p">(</span><span class="nb">- </span><span class="nv">n</span> <span class="mi">1</span><span class="p">))))))</span>
</span><span class='line'>
</span><span class='line'><span class="p">(</span><span class="nf">factorial</span> <span class="mi">5</span><span class="p">)</span> <span class="c1">; 120</span>
</span></code></pre></td></tr></table></div></figure>


<p>我们刚刚说过了，lambda演算中的函数都是匿名函数，也就是说我们定义的函数名都只是语法糖，真正执行的时候其实会代换成这样（根据lambda演算中的β规约）：</p>

<figure class='code'><figcaption><span>factorial.js</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">n</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>  <span class="k">return</span> <span class="nx">n</span> <span class="o">==</span> <span class="mi">1</span> <span class="o">?</span> <span class="mi">1</span> <span class="o">:</span> <span class="nx">n</span> <span class="o">*</span> <span class="nx">factorial</span><span class="p">(</span><span class="nx">n</span> <span class="o">-</span> <span class="mi">1</span><span class="p">);</span>
</span><span class='line'><span class="p">})(</span><span class="mi">5</span><span class="p">);</span>
</span></code></pre></td></tr></table></div></figure>




<figure class='code'><figcaption><span>factorial.clj</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='clojure'><span class='line'><span class="p">((</span><span class="k">fn </span><span class="p">[</span><span class="nv">n</span><span class="p">]</span> <span class="p">(</span><span class="k">if </span><span class="p">(</span><span class="nb">= </span><span class="nv">n</span> <span class="mi">1</span><span class="p">)</span> <span class="mi">1</span>
</span><span class='line'>             <span class="p">(</span><span class="nb">* </span><span class="nv">n</span> <span class="p">(</span><span class="nf">factorial</span> <span class="p">(</span><span class="nb">- </span><span class="nv">n</span> <span class="mi">1</span><span class="p">)))))</span>
</span><span class='line'> <span class="mi">5</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure>


<p>相信到这里大家就能看到问题了，我们用到的函数是没有定义完整的，也就是说在这里，<code>factorial(n - 1)</code>解释器是没法处理的。</p>

<p>那么，为什么我们在C++和Java这样的语言里面可以这么写呢？因为他们是有！名！字！的，在编译的时候编译器会对每个函数生成一个符号，在调用的地方会保存目标函数的符号引用，在运行的时候就能确定被调用函数的地址了。</p>

<h2>Y Combinator的数学意义</h2>

<p>为了解决这个问题，Curry根据函数的不动点，推导出了Y Combinator，从而在理论上让递归问题有了解决的可能。</p>

<p>我们先来看看什么是一个函数的不动点：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>∀F ∃X -> FX = X</span></code></pre></td></tr></table></div></figure>


<p>这句话的意思是对于任意一个函数<code>F</code>，存在一个<code>X</code>，使得<code>F(X) = X</code>。举个栗子，对于抛物线函数<code>f(x) = x^2</code>，它的不动点有两个，<code>0</code>和<code>1</code>，因为<code>f(0) = 0</code>，<code>f(1) = 1</code>。</p>

<p>在这基础上，Curry定义了这样一个函数：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>Y = λf.((λx.f(xx))(λx.f(xx)))</span></code></pre></td></tr></table></div></figure>


<p>他能够达到这样一个目的：对任意的<code>F</code>，都能满足<code>F(YF) = YF</code>。</p>

<h2>Y Combinator的工程意义</h2>

<p>那么这个表达式和我们的递归函数之间有什么关系呢？就这样看确实很难看出他们之间有什么关系，那就先不管他，我们先把递归函数的问题解决了。</p>

<p>我们知道，Lambda演算是与图灵机具有相同计算能力的，那么既然基于图灵机模型发展出来的语言能写递归，我们就应该坚信函数式语言也能写递归。</p>

<p>怎么写？</p>

<p>还是以阶乘为例子，我们可以试着用重构的方式来做。重构之前，我们应该先在心里有个大致的方向：1. 函数要匿名；2. 要尽量通用。</p>

<p>好了，那我们现在就开始重构。</p>

<h3>1. 先写一个“正常”的递归函数</h3>

<figure class='code'><figcaption><span>factorial.js</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="kd">var</span> <span class="nx">factorial</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">n</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>  <span class="k">return</span> <span class="nx">n</span> <span class="o">==</span> <span class="mi">1</span> <span class="o">?</span> <span class="mi">1</span> <span class="o">:</span> <span class="nx">n</span> <span class="o">*</span> <span class="nx">factorial</span><span class="p">(</span><span class="nx">n</span> <span class="o">-</span> <span class="mi">1</span><span class="p">);</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="nx">factorial</span><span class="p">(</span><span class="mi">5</span><span class="p">);</span> <span class="c1">// 120</span>
</span></code></pre></td></tr></table></div></figure>




<figure class='code'><figcaption><span>factorial.clj</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='clojure'><span class='line'><span class="p">(</span><span class="k">def </span><span class="nv">factorial</span>
</span><span class='line'>  <span class="p">(</span><span class="k">fn </span><span class="p">[</span><span class="nv">n</span><span class="p">]</span> <span class="p">(</span><span class="k">if </span><span class="p">(</span><span class="nb">= </span><span class="nv">n</span> <span class="mi">1</span><span class="p">)</span> <span class="mi">1</span>
</span><span class='line'>             <span class="p">(</span><span class="nb">* </span><span class="nv">n</span> <span class="p">(</span><span class="nf">factorial</span> <span class="p">(</span><span class="nb">- </span><span class="nv">n</span> <span class="mi">1</span><span class="p">))))))</span>
</span><span class='line'>
</span><span class='line'><span class="p">(</span><span class="nf">factorial</span> <span class="mi">5</span><span class="p">)</span> <span class="c1">; 120</span>
</span></code></pre></td></tr></table></div></figure>


<h3>2. 把自己作为参数</h3>

<p>我们说过了上面这个函数式有问题的，问题就在于函数内部的<code>factorial</code>调用是在调用一个还没有定义出来的函数。那么，我们就干脆不让他调用自己了，而是去调用一个已经定义好的函数不就好了吗？那么如何来调用一个定义好了的函数呢？</p>

<p>把自己作为参数传进去嘛。也就是说给<code>factorial</code>函数增加一个参数，这个参数是一个函数，用来计算<code>(n - 1)</code>的递归。代码如下：</p>

<figure class='code'><figcaption><span>factorial.js</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="kd">var</span> <span class="nx">factorial</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">f</span><span class="p">,</span> <span class="nx">n</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>  <span class="k">return</span> <span class="nx">n</span> <span class="o">==</span> <span class="mi">1</span> <span class="o">?</span> <span class="mi">1</span> <span class="o">:</span> <span class="nx">n</span> <span class="o">*</span> <span class="nx">f</span><span class="p">(</span><span class="nx">f</span><span class="p">,</span> <span class="nx">n</span> <span class="o">-</span> <span class="mi">1</span><span class="p">);</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="nx">factorial</span><span class="p">(</span><span class="nx">factorial</span><span class="p">,</span> <span class="mi">5</span><span class="p">);</span> <span class="c1">// 120</span>
</span></code></pre></td></tr></table></div></figure>




<figure class='code'><figcaption><span>factorial.clj</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='clojure'><span class='line'><span class="p">(</span><span class="k">def </span><span class="nv">factorial</span>
</span><span class='line'>  <span class="p">(</span><span class="k">fn </span><span class="p">[</span><span class="nv">f</span> <span class="nv">n</span><span class="p">]</span> <span class="p">(</span><span class="k">if </span><span class="p">(</span><span class="nb">= </span><span class="nv">n</span> <span class="mi">1</span><span class="p">)</span> <span class="mi">1</span>
</span><span class='line'>             <span class="p">(</span><span class="nb">* </span><span class="nv">n</span> <span class="p">(</span><span class="nf">f</span> <span class="nv">f</span> <span class="p">(</span><span class="nb">- </span><span class="nv">n</span> <span class="mi">1</span><span class="p">))))))</span>
</span><span class='line'>
</span><span class='line'><span class="p">(</span><span class="nf">factorial</span> <span class="nv">factorial</span> <span class="mi">5</span><span class="p">)</span> <span class="c1">; 120</span>
</span></code></pre></td></tr></table></div></figure>


<h3>3. 柯里化</h3>

<p>我们刚刚的修改让<code>factorial</code>函数有了两个参数，这是不好的。函数式编程里面，我们应该让所有的函数都只有一个参数，这个过程就是上文提到的柯里化。关于他的好处我们就不展开了，有兴趣的同志可以去看看<a href="https://en.wikipedia.org/wiki/Currying">这篇文章</a>。代码如下：</p>

<figure class='code'><figcaption><span>factorial.js</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="kd">var</span> <span class="nx">factorial</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">f</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>  <span class="k">return</span> <span class="kd">function</span><span class="p">(</span><span class="nx">n</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="k">return</span> <span class="nx">n</span> <span class="o">==</span> <span class="mi">1</span> <span class="o">?</span> <span class="mi">1</span> <span class="o">:</span> <span class="nx">n</span> <span class="o">*</span> <span class="nx">f</span><span class="p">(</span><span class="nx">f</span><span class="p">)(</span><span class="nx">n</span> <span class="o">-</span> <span class="mi">1</span><span class="p">);</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="nx">factorial</span><span class="p">(</span><span class="nx">factorial</span><span class="p">)(</span><span class="mi">5</span><span class="p">);</span> <span class="c1">// 120</span>
</span></code></pre></td></tr></table></div></figure>




<figure class='code'><figcaption><span>factorial.clj</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='clojure'><span class='line'><span class="p">(</span><span class="k">def </span><span class="nv">factorial</span>
</span><span class='line'>  <span class="p">(</span><span class="k">fn </span><span class="p">[</span><span class="nv">f</span><span class="p">]</span> <span class="p">(</span><span class="k">fn </span><span class="p">[</span><span class="nv">n</span><span class="p">]</span> <span class="p">(</span><span class="k">if </span><span class="p">(</span><span class="nb">= </span><span class="nv">n</span> <span class="mi">1</span><span class="p">)</span> <span class="mi">1</span>
</span><span class='line'>                    <span class="p">(</span><span class="nb">* </span><span class="nv">n</span> <span class="p">((</span><span class="nf">f</span> <span class="nv">f</span><span class="p">)</span> <span class="p">(</span><span class="nb">- </span><span class="nv">n</span> <span class="mi">1</span><span class="p">)))))))</span>
</span><span class='line'>
</span><span class='line'><span class="p">((</span><span class="nf">factorial</span> <span class="nv">factorial</span><span class="p">)</span> <span class="mi">5</span><span class="p">)</span> <span class="c1">; 120</span>
</span></code></pre></td></tr></table></div></figure>


<h3>4. 去除重复的自身调用</h3>

<p>我们看到，这两段代码可以工作了，但是还是有个问题，就是调用起来太恶心，<code>factorial(factorial)(n)</code>或者<code>((factorial factorial) n)</code>都把自己传了两遍，我们可以把这段代码抽成一个函数，从而让最终用户的调用更优雅一些。代码如下：</p>

<figure class='code'><figcaption><span>factorial.js</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="kd">var</span> <span class="nx">self_invoke</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">f</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>  <span class="k">return</span> <span class="nx">f</span><span class="p">(</span><span class="nx">f</span><span class="p">);</span>
</span><span class='line'><span class="p">};</span>
</span><span class='line'>
</span><span class='line'><span class="kd">var</span> <span class="nx">factorial</span> <span class="o">=</span> <span class="nx">self_invoke</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">f</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>  <span class="k">return</span> <span class="kd">function</span><span class="p">(</span><span class="nx">n</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="k">return</span> <span class="nx">n</span> <span class="o">==</span> <span class="mi">1</span> <span class="o">?</span> <span class="mi">1</span> <span class="o">:</span> <span class="nx">n</span> <span class="o">*</span> <span class="nx">self_invoke</span><span class="p">(</span><span class="nx">f</span><span class="p">)(</span><span class="nx">n</span> <span class="o">-</span> <span class="mi">1</span><span class="p">);</span>
</span><span class='line'>  <span class="p">};</span>
</span><span class='line'><span class="p">});</span>
</span><span class='line'>
</span><span class='line'><span class="nx">factorial</span><span class="p">(</span><span class="mi">5</span><span class="p">);</span> <span class="c1">// 120</span>
</span></code></pre></td></tr></table></div></figure>




<figure class='code'><figcaption><span>factorial.clj</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
</pre></td><td class='code'><pre><code class='clojure'><span class='line'><span class="p">(</span><span class="k">def </span><span class="nv">self-invoke</span>
</span><span class='line'>  <span class="p">(</span><span class="k">fn </span><span class="p">[</span><span class="nv">f</span><span class="p">]</span> <span class="p">(</span><span class="nf">f</span> <span class="nv">f</span><span class="p">)))</span>
</span><span class='line'>
</span><span class='line'><span class="p">(</span><span class="k">def </span><span class="nv">factorial</span>
</span><span class='line'>  <span class="p">(</span><span class="nf">self-invoke</span>
</span><span class='line'>   <span class="p">(</span><span class="k">fn </span><span class="p">[</span><span class="nv">f</span><span class="p">]</span>
</span><span class='line'>     <span class="p">(</span><span class="k">fn </span><span class="p">[</span><span class="nv">n</span><span class="p">]</span> <span class="p">(</span><span class="k">if </span><span class="p">(</span><span class="nb">= </span><span class="nv">n</span> <span class="mi">1</span><span class="p">)</span> <span class="mi">1</span>
</span><span class='line'>                <span class="p">(</span><span class="nb">* </span><span class="nv">n</span> <span class="p">((</span><span class="nf">self-invoke</span> <span class="nv">f</span><span class="p">)</span> <span class="p">(</span><span class="nb">- </span><span class="nv">n</span> <span class="mi">1</span><span class="p">))))))))</span>
</span><span class='line'>
</span><span class='line'><span class="p">(</span><span class="nf">factorial</span> <span class="mi">5</span><span class="p">)</span> <span class="c1">; 120</span>
</span></code></pre></td></tr></table></div></figure>


<h3>5. Inline</h3>

<p>上面的代码定义了两个函数，其中的<code>self-invoke</code>是用的有名字的调用，我们的重构目标是使用匿名函数，所以这一步比较简单，就是把<code>self-invoke</code>函数inline。代码如下：</p>

<figure class='code'><figcaption><span>factorial.js</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="kd">var</span> <span class="nx">factorial</span> <span class="o">=</span> <span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">self_invoke</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>  <span class="k">return</span> <span class="nx">self_invoke</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">f</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="k">return</span> <span class="kd">function</span><span class="p">(</span><span class="nx">n</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>      <span class="k">return</span> <span class="nx">n</span> <span class="o">==</span> <span class="mi">1</span> <span class="o">?</span> <span class="mi">1</span> <span class="o">:</span> <span class="nx">n</span> <span class="o">*</span> <span class="nx">self_invoke</span><span class="p">(</span><span class="nx">f</span><span class="p">)(</span><span class="nx">n</span> <span class="o">-</span> <span class="mi">1</span><span class="p">);</span>
</span><span class='line'>    <span class="p">};</span>
</span><span class='line'>  <span class="p">});</span>
</span><span class='line'><span class="p">})(</span><span class="kd">function</span><span class="p">(</span><span class="nx">f</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>  <span class="k">return</span> <span class="nx">f</span><span class="p">(</span><span class="nx">f</span><span class="p">);</span>
</span><span class='line'><span class="p">});</span>
</span><span class='line'>
</span><span class='line'><span class="nx">factorial</span><span class="p">(</span><span class="mi">5</span><span class="p">);</span> <span class="c1">// 120</span>
</span></code></pre></td></tr></table></div></figure>




<figure class='code'><figcaption><span>factorial.clj</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
</pre></td><td class='code'><pre><code class='clojure'><span class='line'><span class="p">(</span><span class="k">def </span><span class="nv">factorial</span>
</span><span class='line'>  <span class="p">((</span><span class="k">fn </span><span class="p">[</span><span class="nv">self-invoke</span><span class="p">]</span>
</span><span class='line'>     <span class="p">(</span><span class="nf">self-invoke</span>
</span><span class='line'>      <span class="p">(</span><span class="k">fn </span><span class="p">[</span><span class="nv">f</span><span class="p">]</span>
</span><span class='line'>        <span class="p">(</span><span class="k">fn </span><span class="p">[</span><span class="nv">n</span><span class="p">]</span>
</span><span class='line'>          <span class="p">(</span><span class="k">if </span><span class="p">(</span><span class="nb">= </span><span class="nv">n</span> <span class="mi">1</span><span class="p">)</span> <span class="mi">1</span>
</span><span class='line'>              <span class="p">(</span><span class="nb">* </span><span class="nv">n</span> <span class="p">((</span><span class="nf">self-invoke</span> <span class="nv">f</span><span class="p">)</span> <span class="p">(</span><span class="nb">- </span><span class="nv">n</span> <span class="mi">1</span><span class="p">))))))))</span>
</span><span class='line'>   <span class="p">(</span><span class="k">fn </span><span class="p">[</span><span class="nv">f</span><span class="p">]</span> <span class="p">(</span><span class="nf">f</span> <span class="nv">f</span><span class="p">))))</span>
</span><span class='line'>
</span><span class='line'><span class="p">(</span><span class="nf">factorial</span> <span class="mi">5</span><span class="p">)</span> <span class="c1">; 120</span>
</span></code></pre></td></tr></table></div></figure>


<h3>6.抽出伪递归</h3>

<p>我们看到，上面这段代码中</p>

<figure class='code'><figcaption><span>factorial.js</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="kd">function</span><span class="p">(</span><span class="nx">n</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>  <span class="k">return</span> <span class="nx">n</span> <span class="o">==</span> <span class="mi">1</span> <span class="o">?</span> <span class="mi">1</span> <span class="o">:</span> <span class="nx">n</span> <span class="o">*</span> <span class="nx">self_invoke</span><span class="p">(</span><span class="nx">f</span><span class="p">)(</span><span class="nx">n</span> <span class="o">-</span> <span class="mi">1</span><span class="p">);</span>
</span><span class='line'><span class="p">};</span>
</span></code></pre></td></tr></table></div></figure>




<figure class='code'><figcaption><span>factorial.clj</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='clojure'><span class='line'><span class="p">(</span><span class="k">fn </span><span class="p">[</span><span class="nv">n</span><span class="p">]</span>
</span><span class='line'>  <span class="p">(</span><span class="k">if </span><span class="p">(</span><span class="nb">= </span><span class="nv">n</span> <span class="mi">1</span><span class="p">)</span> <span class="mi">1</span>
</span><span class='line'>      <span class="p">(</span><span class="nb">* </span><span class="nv">n</span> <span class="p">((</span><span class="nf">self-invoke</span> <span class="nv">f</span><span class="p">)</span> <span class="p">(</span><span class="nb">- </span><span class="nv">n</span> <span class="mi">1</span><span class="p">)))))</span>
</span></code></pre></td></tr></table></div></figure>


<p>这个结构跟我们最开始的递归函数很像，唯一的区别是这里用<code>self_invoke(f)</code>代替了递归函数中的函数名调用。那么我们可以试着把这一点再抽出来，写出我们最开始的递归函数的样子：</p>

<figure class='code'><figcaption><span>factorial.js</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">self</span><span class="p">){</span>
</span><span class='line'>  <span class="k">return</span> <span class="kd">function</span><span class="p">(</span><span class="nx">n</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="k">return</span> <span class="nx">n</span> <span class="o">==</span> <span class="mi">1</span> <span class="o">?</span> <span class="mi">1</span> <span class="o">:</span> <span class="nx">n</span> <span class="o">*</span> <span class="nx">self</span><span class="p">(</span><span class="nx">n</span> <span class="o">-</span> <span class="mi">1</span><span class="p">);</span>
</span><span class='line'>  <span class="p">};</span>
</span><span class='line'><span class="p">})(</span><span class="kd">function</span><span class="p">(</span><span class="nx">m</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>  <span class="k">return</span> <span class="nx">self_invoke</span><span class="p">(</span><span class="nx">f</span><span class="p">)(</span><span class="nx">m</span><span class="p">);</span>
</span><span class='line'><span class="p">}));</span>
</span></code></pre></td></tr></table></div></figure>




<figure class='code'><figcaption><span>factorial.clj</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='clojure'><span class='line'><span class="p">((</span><span class="k">fn </span><span class="p">[</span><span class="nv">self</span><span class="p">]</span>
</span><span class='line'>  <span class="p">(</span><span class="k">fn </span><span class="p">[</span><span class="nv">n</span><span class="p">]</span>
</span><span class='line'>    <span class="p">(</span><span class="k">if </span><span class="p">(</span><span class="nb">= </span><span class="nv">n</span> <span class="mi">1</span><span class="p">)</span> <span class="mi">1</span>
</span><span class='line'>        <span class="p">(</span><span class="nb">* </span><span class="nv">n</span> <span class="p">(</span><span class="nf">self</span> <span class="p">(</span><span class="nb">- </span><span class="nv">n</span> <span class="mi">1</span><span class="p">)))))</span>
</span><span class='line'> <span class="p">(</span><span class="k">fn </span><span class="p">[</span><span class="nv">m</span><span class="p">]</span> <span class="p">((</span><span class="nf">self-invoke</span> <span class="nv">f</span><span class="p">)</span> <span class="nv">m</span><span class="p">)))</span>
</span></code></pre></td></tr></table></div></figure>


<p>这样我们就能很清楚的看到，这一部分代码：</p>

<figure class='code'><figcaption><span>factorial.js</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="kd">function</span><span class="p">(</span><span class="nx">self</span><span class="p">){</span>
</span><span class='line'>  <span class="k">return</span> <span class="kd">function</span><span class="p">(</span><span class="nx">n</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="k">return</span> <span class="nx">n</span> <span class="o">==</span> <span class="mi">1</span> <span class="o">?</span> <span class="mi">1</span> <span class="o">:</span> <span class="nx">n</span> <span class="o">*</span> <span class="nx">self</span><span class="p">(</span><span class="nx">n</span> <span class="o">-</span> <span class="mi">1</span><span class="p">);</span>
</span><span class='line'>  <span class="p">};</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>




<figure class='code'><figcaption><span>factorial.clj</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
</pre></td><td class='code'><pre><code class='clojure'><span class='line'><span class="p">(</span><span class="k">fn </span><span class="p">[</span><span class="nv">self</span><span class="p">]</span>
</span><span class='line'>  <span class="p">(</span><span class="k">fn </span><span class="p">[</span><span class="nv">n</span><span class="p">]</span>
</span><span class='line'>    <span class="p">(</span><span class="k">if </span><span class="p">(</span><span class="nb">= </span><span class="nv">n</span> <span class="mi">1</span><span class="p">)</span> <span class="mi">1</span>
</span><span class='line'>        <span class="p">(</span><span class="nb">* </span><span class="nv">n</span> <span class="p">(</span><span class="nf">self</span> <span class="p">(</span><span class="nb">- </span><span class="nv">n</span> <span class="mi">1</span><span class="p">))))))</span>
</span></code></pre></td></tr></table></div></figure>


<p>简直跟我们直接写出来的递归函数“一摸一样”，唯一的区别是这里增加了一层函数（参数是self）。很明显，如果我们写了一个直接递归的阶乘函数，我们可以很容易的把这个函数改写成上面的样子。更进一步，如果我们写了任意一个递归函数，我们也能很轻易的把它改写成上面的样子。我们把这个函数就叫做<code>伪递归函数</code>。</p>

<p>这个<code>伪递归函数</code>在我们推导过程中是一个<code>可变量</code>，如果能够把它提取出来，那么剩下的就是<code>不变量</code>。也就是说，我们可以把这个<code>不变量</code>应用到任何一个<code>伪递归函数</code>上面，就能得到我们真正可以递归的函数了。</p>

<p>代码如下：</p>

<figure class='code'><figcaption><span>factorial.js</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="kd">var</span> <span class="nx">fake_factorial</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">self</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>  <span class="k">return</span> <span class="kd">function</span><span class="p">(</span><span class="nx">n</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="k">return</span> <span class="nx">n</span> <span class="o">==</span> <span class="mi">1</span> <span class="o">?</span> <span class="mi">1</span> <span class="o">:</span> <span class="nx">n</span> <span class="o">*</span> <span class="nx">self</span><span class="p">(</span><span class="nx">n</span> <span class="o">-</span> <span class="mi">1</span><span class="p">);</span>
</span><span class='line'>  <span class="p">};</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="kd">var</span> <span class="nx">y</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">fake_rec</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>  <span class="k">return</span> <span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">self_invoke</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="k">return</span> <span class="nx">self_invoke</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">f</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>      <span class="k">return</span> <span class="nx">fake_rec</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">m</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>        <span class="k">return</span> <span class="nx">self_invoke</span><span class="p">(</span><span class="nx">f</span><span class="p">)(</span><span class="nx">m</span><span class="p">);</span>
</span><span class='line'>      <span class="p">});</span>
</span><span class='line'>    <span class="p">});</span>
</span><span class='line'>  <span class="p">})(</span><span class="kd">function</span><span class="p">(</span><span class="nx">f</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="k">return</span> <span class="nx">f</span><span class="p">(</span><span class="nx">f</span><span class="p">);</span>
</span><span class='line'>  <span class="p">});</span>
</span><span class='line'><span class="p">};</span>
</span><span class='line'>
</span><span class='line'><span class="kd">var</span> <span class="nx">factorial</span> <span class="o">=</span> <span class="nx">y</span><span class="p">(</span><span class="nx">fake_factorial</span><span class="p">);</span>
</span><span class='line'>
</span><span class='line'><span class="nx">factorial</span><span class="p">(</span><span class="mi">5</span><span class="p">);</span> <span class="c1">// 120</span>
</span></code></pre></td></tr></table></div></figure>




<figure class='code'><figcaption><span>factorial.clj</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
</pre></td><td class='code'><pre><code class='clojure'><span class='line'><span class="p">(</span><span class="k">def </span><span class="nv">fake-factorial</span>
</span><span class='line'>  <span class="p">(</span><span class="k">fn </span><span class="p">[</span><span class="nv">self</span><span class="p">]</span>
</span><span class='line'>    <span class="p">(</span><span class="k">fn </span><span class="p">[</span><span class="nv">n</span><span class="p">]</span>
</span><span class='line'>      <span class="p">(</span><span class="k">if </span><span class="p">(</span><span class="nb">= </span><span class="nv">n</span> <span class="mi">1</span><span class="p">)</span> <span class="mi">1</span>
</span><span class='line'>          <span class="p">(</span><span class="nb">* </span><span class="nv">n</span> <span class="p">(</span><span class="nf">self</span> <span class="p">(</span><span class="nb">- </span><span class="nv">n</span> <span class="mi">1</span><span class="p">)))))))</span>
</span><span class='line'>
</span><span class='line'><span class="p">(</span><span class="k">def </span><span class="nv">y</span>
</span><span class='line'>  <span class="p">(</span><span class="k">fn </span><span class="p">[</span><span class="nv">fake-rec</span><span class="p">]</span> <span class="p">((</span><span class="k">fn </span><span class="p">[</span><span class="nv">self-invoke</span><span class="p">]</span>
</span><span class='line'>                   <span class="p">(</span><span class="nf">self-invoke</span>
</span><span class='line'>                    <span class="p">(</span><span class="k">fn </span><span class="p">[</span><span class="nv">f</span><span class="p">]</span>
</span><span class='line'>                      <span class="p">(</span><span class="nf">fake-rec</span>
</span><span class='line'>                       <span class="p">(</span><span class="k">fn </span><span class="p">[</span><span class="nv">m</span><span class="p">]</span> <span class="p">((</span><span class="nf">self-invoke</span> <span class="nv">f</span><span class="p">)</span> <span class="nv">m</span><span class="p">))))))</span>
</span><span class='line'>                 <span class="p">(</span><span class="k">fn </span><span class="p">[</span><span class="nv">f</span><span class="p">]</span> <span class="p">(</span><span class="nf">f</span> <span class="nv">f</span><span class="p">)))))</span>
</span><span class='line'>
</span><span class='line'><span class="p">(</span><span class="k">def </span><span class="nv">factorial</span>
</span><span class='line'>  <span class="p">(</span><span class="nf">y</span> <span class="nv">fake-factorial</span><span class="p">))</span>
</span><span class='line'>
</span><span class='line'><span class="p">(</span><span class="nf">factorial</span> <span class="mi">5</span><span class="p">)</span> <span class="c1">; 120</span>
</span></code></pre></td></tr></table></div></figure>


<p>这里，<code>y</code>是不变量，<code>fake-factorial</code>是变量。将任意的一个伪递归函数作为实参调用<code>y</code>，就能得到真正的递归函数了。这里的<code>y</code>也就是我们的Y Combinator。</p>

<p>这里推导出来的<code>y</code>还是稍显复杂，我们可以再重构一下，变成这样：</p>

<figure class='code'><figcaption><span>factorial.js</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="kd">var</span> <span class="nx">y</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">fake_rec</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>  <span class="k">return</span> <span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">f</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="k">return</span> <span class="nx">f</span><span class="p">(</span><span class="nx">f</span><span class="p">);</span>
</span><span class='line'>  <span class="p">})(</span><span class="kd">function</span><span class="p">(</span><span class="nx">f</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="k">return</span> <span class="nx">fake_rec</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">x</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>      <span class="k">return</span> <span class="nx">f</span><span class="p">(</span><span class="nx">f</span><span class="p">)(</span><span class="nx">x</span><span class="p">);</span>
</span><span class='line'>    <span class="p">});</span>
</span><span class='line'>  <span class="p">});</span>
</span><span class='line'><span class="p">};</span>
</span></code></pre></td></tr></table></div></figure>




<figure class='code'><figcaption><span>factorial.clj</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
</pre></td><td class='code'><pre><code class='clojure'><span class='line'><span class="p">(</span><span class="k">def </span><span class="nv">y</span>
</span><span class='line'>  <span class="p">(</span><span class="k">fn </span><span class="p">[</span><span class="nv">fake-rec</span><span class="p">]</span>
</span><span class='line'>    <span class="p">((</span><span class="k">fn </span><span class="p">[</span><span class="nv">f</span><span class="p">]</span> <span class="p">(</span><span class="nf">f</span> <span class="nv">f</span><span class="p">))</span> <span class="p">(</span><span class="k">fn </span><span class="p">[</span><span class="nv">f</span><span class="p">]</span>
</span><span class='line'>                     <span class="p">(</span><span class="nf">fake-rec</span> <span class="p">(</span><span class="k">fn </span><span class="p">[</span><span class="nv">x</span><span class="p">]</span> <span class="p">((</span><span class="nf">f</span> <span class="nv">f</span><span class="p">)</span> <span class="nv">x</span><span class="p">)))))))</span>
</span></code></pre></td></tr></table></div></figure>


<h2>总结</h2>

<p>到这里，<code>Y Combinator</code>就已经推导完了。</p>

<p>回到我们最开始的地方，<code>Y Combinator</code>的数学定义是对任意的<code>F</code>，都能满足<code>F(YF) = YF</code>。结合我们的推导，可以看出，这里的<code>F</code>其实就是我们的<code>伪递归函数</code>，<code>YF</code>其实就是我们真实的递归函数。伪递归函数<code>F</code>和我们真正的递归函数<code>f</code>之间的关系就是<code>F(f) = f</code>。</p>

<h2>致谢</h2>

<p>感谢太君的<a href="http://cuipengfei.me/blog/2013/04/09/make-y/">这篇文章</a>的启发</p>

<h2>Reference</h2>

<ul>
<li><a href="http://mindhacks.cn/2006/10/15/cantor-godel-turing-an-eternal-golden-diagonal/">康托尔、哥德尔、图灵——永恒的金色对角线</a>，刘未鹏。</li>
<li><a href="http://cuipengfei.me/blog/2013/04/09/make-y/">如何一步一步推导出Y Combinator</a>，崔鹏飞。</li>
<li><a href="https://github.com/zhewuzhou/js-y-combinator/blob/master/y-combinator.js">y-combinator.js</a>，周哲武。</li>
</ul>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Lisp中cons/car/cdr的几种实现方式]]></title>
    <link href="http://yaodanzhang.com/blog/2015/03/16/three-implementations-of-cons-in-lisp/"/>
    <updated>2015-03-16T15:38:57+08:00</updated>
    <id>http://yaodanzhang.com/blog/2015/03/16/three-implementations-of-cons-in-lisp</id>
    <content type="html"><![CDATA[<h2>基本概念</h2>

<p>在Lisp中，<code>cons</code>、<code>car</code>、<code>cdr</code>是三个原始操作符，Lisp在它们（和另外几个原始操作符）的基础上，定义了一个干净、完整的编程语言。关于Lisp的基本概念，有一篇博客写的非常好，强烈推荐大家去读一读：《<a href="http://daiyuwen.freeshell.org/gb/rol/roots_of_lisp.html">Lisp之根源</a>》</p>

<!--more -->


<h3>Lisp中的一般表达式</h3>

<p>Lisp中的表达式一般是长这样的：<code>(op args)</code>。</p>

<p>简单来说，他的构成方式就是用一对括号括起一些元素，形成一个表，用来表达一个过程。其中，在表里最左边的元素<code>op</code>叫做<em>运算符</em>，其他元素（0个或多个）都叫做<em>运算对象</em>。要得到一个表达式的值，就是将运算符描述的过程应用于<em>实际参数</em>，而所谓的<em>实参</em>就是那些运算对象的值。</p>

<p>给大家一些例子就好明白了：</p>

<figure class='code'><figcaption><span>lisp expressions</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='clojure'><span class='line'><span class="p">(</span><span class="nb">+ </span><span class="mi">1</span> <span class="mi">1</span><span class="p">)</span>  <span class="nv">--&gt;</span> <span class="mi">2</span>
</span><span class='line'><span class="p">(</span><span class="nb">- </span><span class="mi">2</span> <span class="mi">1</span><span class="p">)</span>  <span class="nv">--&gt;</span> <span class="mi">1</span>
</span><span class='line'><span class="p">(</span><span class="nb">* </span><span class="mi">2</span> <span class="mi">3</span><span class="p">)</span>  <span class="nv">--&gt;</span> <span class="mi">6</span>
</span><span class='line'><span class="p">(</span><span class="nb">/ </span><span class="mi">2</span> <span class="mi">1</span><span class="p">)</span>  <span class="nv">--&gt;</span> <span class="mi">2</span>
</span><span class='line'><span class="p">(</span><span class="nb">+ </span><span class="mi">1</span> <span class="mi">2</span> <span class="mi">3</span> <span class="mi">4</span><span class="p">)</span>  <span class="nv">--&gt;</span> <span class="mi">10</span>
</span></code></pre></td></tr></table></div></figure>


<p>我们把运算符放在所有的运算对象左边，这种形式成为<em>前缀表达式</em>。乍看起来怪怪的，但好处多多，这里就不展开了。</p>

<h3>cons</h3>

<p><code>cons</code>接收两个参数，组成了一个有序对（pair）。这是一个很简单的数据结构，但是由于<code>cons</code>的参数可以是任意类型，那么在这基础上就可以组成各种各样的数据结构了。</p>

<p>基本的有序对：</p>

<figure class='code'><figcaption><span>pair</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='clojure'><span class='line'><span class="p">(</span><span class="nb">cons </span><span class="mi">17</span> <span class="mi">29</span><span class="p">)</span>
</span><span class='line'>
</span><span class='line'>  <span class="nv">*</span>
</span><span class='line'> <span class="nb">/ </span><span class="err">\</span>
</span><span class='line'><span class="mi">17</span> <span class="mi">29</span>
</span></code></pre></td></tr></table></div></figure>


<p>链表：</p>

<figure class='code'><figcaption><span>list</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='clojure'><span class='line'><span class="p">(</span><span class="nb">cons </span><span class="mi">3</span> <span class="p">(</span><span class="nb">cons </span><span class="mi">5</span> <span class="p">(</span><span class="nb">cons </span><span class="mi">7</span> <span class="p">(</span><span class="nb">cons </span><span class="mi">9</span> <span class="nv">nil</span><span class="p">))))</span>
</span><span class='line'>
</span><span class='line'> <span class="nv">*--*--*--*--nil</span>
</span><span class='line'> <span class="err">|</span>  <span class="err">|</span>  <span class="err">|</span>  <span class="err">|</span>
</span><span class='line'> <span class="mi">3</span>  <span class="mi">5</span>  <span class="mi">7</span>  <span class="mi">9</span>
</span></code></pre></td></tr></table></div></figure>


<p>二叉树：</p>

<figure class='code'><figcaption><span>tree</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
</pre></td><td class='code'><pre><code class='clojure'><span class='line'><span class="p">(</span><span class="nb">cons </span><span class="p">(</span><span class="nb">cons </span><span class="mi">1</span> <span class="mi">2</span><span class="p">)</span> <span class="p">(</span><span class="nb">cons </span><span class="mi">3</span> <span class="mi">4</span><span class="p">))</span>
</span><span class='line'>
</span><span class='line'>   <span class="nv">*</span>
</span><span class='line'>  <span class="nb">/ </span><span class="err">\</span>
</span><span class='line'> <span class="nb">* </span>  <span class="nv">*</span>
</span><span class='line'><span class="nb">/ </span><span class="sc">\ </span><span class="nb">/ </span><span class="err">\</span>
</span><span class='line'><span class="mi">1</span> <span class="mi">2</span> <span class="mi">3</span> <span class="mi">4</span>
</span></code></pre></td></tr></table></div></figure>


<h3>car/cdr</h3>

<p>对于一个<code>cons</code>组成的有序对，我们可以用<code>car</code>取第一个元素，用<code>cdr</code>取第二个元素。</p>

<p>例子：</p>

<figure class='code'><figcaption><span>car & cdr</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='clojure'><span class='line'><span class="p">(</span><span class="nf">car</span> <span class="p">(</span><span class="nb">cons </span><span class="mi">1</span> <span class="mi">2</span><span class="p">))</span> <span class="nv">--&gt;</span> <span class="mi">1</span>
</span><span class='line'><span class="p">(</span><span class="nf">cdr</span> <span class="p">(</span><span class="nb">cons </span><span class="mi">1</span> <span class="mi">2</span><span class="p">))</span> <span class="nv">--&gt;</span> <span class="mi">2</span>
</span></code></pre></td></tr></table></div></figure>


<h2>如何实现cons/car/cdr</h2>

<p>在一般的Lisp方言里，由于这三个操作太重要，太原始了，为了效率，一般都是直接实现的。但是我们学习的时候还是可以想想如何自己来实现这三个操作的。</p>

<h3>定义需求</h3>

<p>这三个操作的描述已经在上面了，要实现他们，我们的需求就是：如果用<code>cons</code>把两个元素绑定在一起，那个可以用<code>car</code>和<code>cdr</code>把他们分别提取出来。</p>

<p>那么可以写一个很简单的测试来测我们的实现：</p>

<figure class='code'><figcaption><span>cons_test.clj</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
</pre></td><td class='code'><pre><code class='clojure'><span class='line'><span class="p">(</span><span class="nf">deftest</span> <span class="nv">cons-test</span>
</span><span class='line'>  <span class="p">(</span><span class="nf">testing</span> <span class="s">&quot;test my cons&quot;</span>
</span><span class='line'>    <span class="p">(</span><span class="nf">is</span> <span class="p">(</span><span class="nb">= </span><span class="p">(</span><span class="nf">car</span> <span class="p">(</span><span class="nb">cons </span><span class="mi">17</span> <span class="mi">29</span><span class="p">))</span> <span class="mi">17</span><span class="p">))</span>
</span><span class='line'>    <span class="p">(</span><span class="nf">is</span> <span class="p">(</span><span class="nb">= </span><span class="p">(</span><span class="nf">cdr</span> <span class="p">(</span><span class="nb">cons </span><span class="mi">17</span> <span class="mi">29</span><span class="p">))</span> <span class="mi">29</span><span class="p">))))</span>
</span></code></pre></td></tr></table></div></figure>


<h3>普通实现</h3>

<p>直接上代码了：</p>

<figure class='code'><figcaption><span>cons.clj</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
</pre></td><td class='code'><pre><code class='clojure'><span class='line'><span class="p">(</span><span class="kd">defn </span><span class="nv">cons</span>
</span><span class='line'>  <span class="p">[</span><span class="nv">a</span>, <span class="nv">b</span><span class="p">]</span>
</span><span class='line'>  <span class="p">(</span><span class="k">fn </span><span class="p">[</span><span class="nv">m</span><span class="p">]</span> <span class="p">(</span><span class="k">if </span><span class="p">(</span><span class="nb">= </span><span class="nv">m</span> <span class="mi">0</span><span class="p">)</span> <span class="nv">a</span> <span class="nv">b</span><span class="p">)))</span>
</span><span class='line'>
</span><span class='line'><span class="p">(</span><span class="kd">defn </span><span class="nv">car</span>
</span><span class='line'>  <span class="p">[</span><span class="nv">c</span><span class="p">]</span>
</span><span class='line'>  <span class="p">(</span><span class="nf">c</span> <span class="mi">0</span><span class="p">))</span>
</span><span class='line'>
</span><span class='line'><span class="p">(</span><span class="kd">defn </span><span class="nv">cdr</span>
</span><span class='line'>  <span class="p">[</span><span class="nv">c</span><span class="p">]</span>
</span><span class='line'>  <span class="p">(</span><span class="nf">c</span> <span class="mi">1</span><span class="p">))</span>
</span></code></pre></td></tr></table></div></figure>


<p>我们说过了，<code>cons</code>实际上是用来定义一种数据，这个数据由两个元素组成。但是，我们的实现完全没有用到任何的数据结构，只是用过程就把他实现了。</p>

<p>这里面<code>cons</code>操作的结果不是数据，而是一个函数，这里直接返回了一个lambda表达式，当然也可以返回一个有名字的函数，像下面这样：</p>

<figure class='code'><figcaption><span>cons.clj</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
</pre></td><td class='code'><pre><code class='clojure'><span class='line'><span class="p">(</span><span class="kd">defn </span><span class="nv">cons</span>
</span><span class='line'>  <span class="p">[</span><span class="nv">a</span>, <span class="nv">b</span><span class="p">]</span>
</span><span class='line'>  <span class="p">(</span><span class="kd">defn </span><span class="nv">dispatch</span>
</span><span class='line'>    <span class="p">[</span><span class="nv">m</span><span class="p">]</span>
</span><span class='line'>    <span class="p">(</span><span class="k">if </span><span class="p">(</span><span class="nb">= </span><span class="nv">m</span> <span class="mi">0</span><span class="p">)</span> <span class="nv">a</span> <span class="nv">b</span><span class="p">))</span>
</span><span class='line'>  <span class="nv">dispath</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure>


<p>比如c的值是<code>(cons 3 4)</code>，那么c实际上是这样一个函数<code>(λ [m] (if (= m 0) 3 4)</code>，他的意思是给一个参数m，如果m的值是0，那么返回3（第一个元素），否则返回4（第二个元素）。再看看我们定义的<code>car</code>，他实际上是将0作为参数调用上面这个函数，结果返回3。<code>cdr</code>也是一样的道理。</p>

<p>这就是一个纯过程的实现，但是这里面m啊，0啊这些东西看起来还是很恶心，于是我们有了另外一个版本。</p>

<h3>文艺实现</h3>

<p>还是先上代码：</p>

<figure class='code'><figcaption><span>cons.clj</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
</pre></td><td class='code'><pre><code class='clojure'><span class='line'><span class="p">(</span><span class="kd">defn </span><span class="nv">cons</span>
</span><span class='line'>  <span class="p">[</span><span class="nv">a</span>, <span class="nv">b</span><span class="p">]</span>
</span><span class='line'>  <span class="p">(</span><span class="k">fn </span><span class="p">[</span><span class="nv">f</span><span class="p">]</span> <span class="p">(</span><span class="nf">f</span> <span class="nv">a</span> <span class="nv">b</span><span class="p">)))</span>
</span><span class='line'>
</span><span class='line'><span class="p">(</span><span class="kd">defn </span><span class="nv">car</span>
</span><span class='line'>  <span class="p">[</span><span class="nv">c</span><span class="p">]</span>
</span><span class='line'>  <span class="p">(</span><span class="nf">c</span> <span class="p">(</span><span class="k">fn </span><span class="p">[</span><span class="nv">a</span>, <span class="nv">b</span><span class="p">]</span> <span class="nv">a</span><span class="p">)))</span>
</span><span class='line'>
</span><span class='line'><span class="p">(</span><span class="kd">defn </span><span class="nv">cdr</span>
</span><span class='line'>  <span class="p">[</span><span class="nv">c</span><span class="p">]</span>
</span><span class='line'>  <span class="p">(</span><span class="nf">c</span> <span class="p">(</span><span class="k">fn </span><span class="p">[</span><span class="nv">a</span>, <span class="nv">b</span><span class="p">]</span> <span class="nv">b</span><span class="p">)))</span>
</span></code></pre></td></tr></table></div></figure>


<p>我们先理解<code>cons</code>。如果c的值是<code>(cons 3 4)</code>，那么c实际上是这样一个函数<code>(λ [f] (f 3 4))</code>，这个函数接收一个函数f作为参数，返回值是以参数[3, 4]调用f的结果。接下来看<code>car</code>，<code>car</code>实际上也是调用c，而这个实参是一个函数<code>(λ [a, b] a)</code>，这个函数接收两个参数，并且返回第一个参数。接下来我们把这个函数代入c，得到的是这样的一个表达式：<code>((λ [f] (f 3 4)) (λ [a, b] a))</code>，由于前面的<code>(λ [f] (f 3 4))</code>是操作符，将后面的实参代入并替换f过后就会得到：<code>((λ [a, b] a) 3 4)</code>，这样再进行一次实参代入，大家就很容易的能看出结果应该是3了。<code>cdr</code>也是一样的道理。</p>

<h3>二逼实现</h3>

<p>如果限定一下，<code>cons</code>的参数只能是非负整数，那么还可以用这样的方式来定义：<code>(cons a b) --&gt; 2^a * 3^b</code>，即用2的a次方和3的b次方的乘积表示。那么只需要将<code>cons</code>结果对2和3进行质因子分界，就能得到<code>car</code>和<code>cdr</code>。</p>

<p>代码在这里：</p>

<figure class='code'><figcaption><span>cons.clj</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
</pre></td><td class='code'><pre><code class='clojure'><span class='line'><span class="p">(</span><span class="kd">defn </span><span class="nv">cons</span>
</span><span class='line'>  <span class="p">[</span><span class="nv">a</span>, <span class="nv">b</span><span class="p">]</span>
</span><span class='line'>
</span><span class='line'>  <span class="p">(</span><span class="kd">defn </span><span class="nv">sqr</span>
</span><span class='line'>    <span class="p">[</span><span class="nv">x</span>, <span class="nv">n</span><span class="p">]</span>
</span><span class='line'>    <span class="p">(</span><span class="nb">cond </span><span class="p">(</span><span class="nb">= </span><span class="nv">n</span> <span class="mi">0</span><span class="p">)</span> <span class="mi">1</span>
</span><span class='line'>          <span class="p">(</span><span class="nb">= </span><span class="nv">n</span> <span class="mi">1</span><span class="p">)</span> <span class="nv">x</span>
</span><span class='line'>          <span class="p">(</span><span class="nb">&gt; </span><span class="nv">n</span> <span class="mi">1</span><span class="p">)</span> <span class="p">(</span><span class="nb">* </span><span class="nv">x</span> <span class="p">(</span><span class="nf">sqr</span> <span class="nv">x</span> <span class="p">(</span><span class="nb">- </span><span class="nv">n</span> <span class="mi">1</span><span class="p">)))</span>
</span><span class='line'>          <span class="ss">:else</span> <span class="p">(</span><span class="nf">throw</span> <span class="p">(</span><span class="nf">.IllegalArgumentException</span> <span class="s">&quot;wrong param&quot;</span><span class="p">))))</span>
</span><span class='line'>
</span><span class='line'>  <span class="p">(</span><span class="nb">* </span><span class="p">(</span><span class="nf">sqr</span> <span class="mi">2</span> <span class="nv">a</span><span class="p">)</span> <span class="p">(</span><span class="nf">sqr</span> <span class="mi">3</span> <span class="nv">b</span><span class="p">)))</span>
</span><span class='line'>
</span><span class='line'><span class="p">(</span><span class="kd">defn </span><span class="nv">factor</span>
</span><span class='line'>  <span class="p">[</span><span class="nv">x</span>, <span class="nv">a</span><span class="p">]</span>
</span><span class='line'>
</span><span class='line'>  <span class="p">(</span><span class="kd">defn </span><span class="nv">iter</span>
</span><span class='line'>    <span class="p">[</span><span class="nv">x</span>, <span class="nv">n</span><span class="p">]</span>
</span><span class='line'>    <span class="p">(</span><span class="k">if </span><span class="p">(</span><span class="nb">= </span><span class="mi">0</span> <span class="p">(</span><span class="nf">mod</span> <span class="nv">x</span> <span class="nv">a</span><span class="p">))</span> <span class="p">(</span><span class="nf">iter</span> <span class="p">(</span><span class="nb">/ </span><span class="nv">x</span> <span class="nv">a</span><span class="p">)</span> <span class="p">(</span><span class="nb">+ </span><span class="nv">n</span> <span class="mi">1</span><span class="p">))</span>
</span><span class='line'>        <span class="nv">n</span><span class="p">))</span>
</span><span class='line'>
</span><span class='line'>  <span class="p">(</span><span class="nf">iter</span> <span class="nv">x</span> <span class="mi">0</span><span class="p">))</span>
</span><span class='line'>
</span><span class='line'><span class="p">(</span><span class="kd">defn </span><span class="nv">car</span>
</span><span class='line'>  <span class="p">[</span><span class="nv">c</span><span class="p">]</span>
</span><span class='line'>  <span class="p">(</span><span class="nf">factor</span> <span class="nv">c</span> <span class="mi">2</span><span class="p">))</span>
</span><span class='line'>
</span><span class='line'><span class="p">(</span><span class="kd">defn </span><span class="nv">cdr</span>
</span><span class='line'>  <span class="p">[</span><span class="nv">c</span><span class="p">]</span>
</span><span class='line'>  <span class="p">(</span><span class="nf">factor</span> <span class="nv">c</span> <span class="mi">3</span><span class="p">))</span>
</span></code></pre></td></tr></table></div></figure>


<h2>总结</h2>

<p><code>cons</code>的这几种实现方式体现的其实就是在Lisp中对数据进行抽象的方式。我们用的是完全过程化的方式，过程的这种使用方法和我们关于数据应该是什么样的直观认识大相径庭。</p>

<p>把过程当作对象去操作，这为我们提供了一种表示复杂数据的能力。</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[如何将50行的if-else块重构成3行]]></title>
    <link href="http://yaodanzhang.com/blog/2014/07/08/how-to-refactor-a-50-lines-if-else-clause-to-3-lines/"/>
    <updated>2014-07-08T10:52:00+08:00</updated>
    <id>http://yaodanzhang.com/blog/2014/07/08/how-to-refactor-a-50-lines-if-else-clause-to-3-lines</id>
    <content type="html"><![CDATA[<h2>背景</h2>

<p>今天下午花了半天时间重构了客户遗留系统中一个<em>一万行</em>的类</p>

<!--more -->


<p>&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;-我是分割线&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;</p>

<p>里面的500行代码。</p>

<p>心情大好，Mark一下。</p>

<h2>业务</h2>

<p>我们做的是一个批发商信息管理系统，提供了一个更新批发商信息的服务，有多个别的系统可以通过调用该服务发送更新批发商信息的请求。</p>

<p>在这里，批发商们有一个很重要的信息叫做公司简介（<em>Summary</em>）。</p>

<p>由于很多业务上的原因（具体原因就不讨论了），其他系统发过来的更新请求有可能是不受信任的（<em>untrusted</em>），因此我们针对<em>Summary</em>添加了一个标志位，如果消息源受信任，就把它设置成<em>trusted</em>，如果不受信任，就设成<em>untrusted</em>。</p>

<blockquote><p>我就这么一说，你就这么一信，这可不是我们真实的客户场景</p>

<p>客户代码自然是不能传上来给大家看的啦，我已经把这里涉及到的业务场景改的面目全非了</p>

<p>费了我老大的劲了</p></blockquote>

<h2>新的业务</h2>

<p>在这个系统运行了十年过后（你没看错，就是<em>十年</em>），客户终于发现，这里面有个Bug！！！</p>

<p><em>举个栗子</em></p>

<hr />

<p>>> 我是一个快乐的批发商，我在一个受信任的系统中更新了我的<em>Summary</em>。</p>

<p>>> 这时候，数据库里面有了我的一个<em>Summary</em>数据，这个数据是正确（<em>trusted</em>）的。</p>

<p>>> 突然有一天，某个奇怪的人想通过某个不受信任的系统更新<em>我</em>的<em>Summary</em>。</p>

<p>>> 当然啦，我们的系统是很人性化的，它会把这个请求标记成<em>untrusted</em>。</p>

<p>>> 但是，奇怪的事情发生了，</p>

<p>>> 系统用这个<em>untrusted</em>的<em>Summary</em>覆盖了我自己提交的<em>Summary</em>（天哪！！！）</p>

<p>>> 又有一天，我登录系统来看我的<em>Summary</em></p>

<p>>> 我不高兴了。。。。。</p>

<hr />

<h2>于是</h2>

<p>客户找到了Dev，让我们改掉这个Bug。</p>

<blockquote><p>很简单嘛， 就是不要让untrusted的信息覆盖trusted嘛，别的逻辑不变嘛</p></blockquote>

<h2>如何改</h2>

<p>我们说过了，这是一个<em>一万行</em>的类。</p>

<blockquote><p>“你不会让我直接在里面改代码吧？”</p>

<p>“当然不会啦！为了愉快的修改，我们会做足前戏的。”</p>

<p>“纳尼？”</p></blockquote>

<p>恩，就是下面这些工（前）作（戏）：</p>

<ol>
<li>在茫茫的代码中找到需要修改的地方，</li>
<li>将需要修改的部分提取到一个单独的类，</li>
<li>给他加测试，让我们的测试覆盖所有可能的情况（需要注意的是，不光要保证行覆盖率和分支覆盖率都要达到100%，而且要覆盖所有的Scenario），</li>
<li>重构抽出来的这部分代码，让一个健康的人可以理解它，</li>
<li>把测试改成我们现在期望的行为，</li>
<li>最后，改掉这个Bug。</li>
</ol>


<p>下面，我们来演示一下。</p>

<h2>找到需要修改的地方</h2>

<p>（好像没什么技巧？那就略过把）</p>

<h2>把找到的东西抽取出来</h2>

<p>嗯，这是我们抽出来的代码：(还没开始改哦亲！)</p>

<figure class='code'><figcaption><span>SummaryUpdateHelper.java</span><a href='https://github.com/YaodanZhang/code-kata/blob/master/src/main/java/com/thoughtworks/kata/refactor/SummaryUpdateHelper.java'>link</a></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
<span class='line-number'>42</span>
<span class='line-number'>43</span>
<span class='line-number'>44</span>
<span class='line-number'>45</span>
<span class='line-number'>46</span>
<span class='line-number'>47</span>
<span class='line-number'>48</span>
<span class='line-number'>49</span>
<span class='line-number'>50</span>
<span class='line-number'>51</span>
<span class='line-number'>52</span>
<span class='line-number'>53</span>
<span class='line-number'>54</span>
<span class='line-number'>55</span>
<span class='line-number'>56</span>
<span class='line-number'>57</span>
<span class='line-number'>58</span>
<span class='line-number'>59</span>
<span class='line-number'>60</span>
<span class='line-number'>61</span>
<span class='line-number'>62</span>
<span class='line-number'>63</span>
<span class='line-number'>64</span>
<span class='line-number'>65</span>
<span class='line-number'>66</span>
<span class='line-number'>67</span>
<span class='line-number'>68</span>
<span class='line-number'>69</span>
<span class='line-number'>70</span>
<span class='line-number'>71</span>
<span class='line-number'>72</span>
<span class='line-number'>73</span>
<span class='line-number'>74</span>
<span class='line-number'>75</span>
<span class='line-number'>76</span>
<span class='line-number'>77</span>
<span class='line-number'>78</span>
<span class='line-number'>79</span>
<span class='line-number'>80</span>
<span class='line-number'>81</span>
<span class='line-number'>82</span>
<span class='line-number'>83</span>
<span class='line-number'>84</span>
<span class='line-number'>85</span>
<span class='line-number'>86</span>
<span class='line-number'>87</span>
<span class='line-number'>88</span>
<span class='line-number'>89</span>
<span class='line-number'>90</span>
<span class='line-number'>91</span>
<span class='line-number'>92</span>
<span class='line-number'>93</span>
<span class='line-number'>94</span>
<span class='line-number'>95</span>
<span class='line-number'>96</span>
<span class='line-number'>97</span>
<span class='line-number'>98</span>
<span class='line-number'>99</span>
<span class='line-number'>100</span>
<span class='line-number'>101</span>
<span class='line-number'>102</span>
<span class='line-number'>103</span>
<span class='line-number'>104</span>
<span class='line-number'>105</span>
<span class='line-number'>106</span>
<span class='line-number'>107</span>
<span class='line-number'>108</span>
<span class='line-number'>109</span>
<span class='line-number'>110</span>
<span class='line-number'>111</span>
<span class='line-number'>112</span>
<span class='line-number'>113</span>
<span class='line-number'>114</span>
<span class='line-number'>115</span>
<span class='line-number'>116</span>
<span class='line-number'>117</span>
<span class='line-number'>118</span>
<span class='line-number'>119</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'><span class="kd">public</span> <span class="kd">class</span> <span class="nc">SummaryUpdateHelper</span> <span class="o">{</span>
</span><span class='line'>    <span class="kd">private</span> <span class="n">Map</span><span class="o">&lt;</span><span class="n">String</span><span class="o">,</span> <span class="n">Object</span><span class="o">&gt;</span> <span class="n">updatedFields</span><span class="o">;</span>
</span><span class='line'>
</span><span class='line'>    <span class="kd">public</span> <span class="nf">SummaryUpdateHelper</span><span class="o">(</span><span class="n">Map</span><span class="o">&lt;</span><span class="n">String</span><span class="o">,</span> <span class="n">Object</span><span class="o">&gt;</span> <span class="n">updatedFields</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="k">this</span><span class="o">.</span><span class="na">updatedFields</span> <span class="o">=</span> <span class="n">updatedFields</span><span class="o">;</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">updateSummary</span><span class="o">(</span><span class="n">Request</span> <span class="n">request</span><span class="o">,</span> <span class="n">Customer</span> <span class="n">customer</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">RequestSummary</span> <span class="n">requestSummary</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="na">getSummary</span><span class="o">();</span>
</span><span class='line'>        <span class="n">Summary</span> <span class="n">summaryFromDb</span> <span class="o">=</span> <span class="n">customer</span><span class="o">.</span><span class="na">getSummary</span><span class="o">();</span>
</span><span class='line'>
</span><span class='line'>        <span class="k">if</span> <span class="o">(</span><span class="n">summaryFromDb</span> <span class="o">==</span> <span class="kc">null</span> <span class="o">&amp;&amp;</span> <span class="n">requestSummary</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>            <span class="n">summaryFromDb</span> <span class="o">=</span> <span class="k">new</span> <span class="n">Summary</span><span class="o">();</span>
</span><span class='line'>        <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>        <span class="k">if</span> <span class="o">(</span><span class="kc">null</span> <span class="o">!=</span> <span class="n">requestSummary</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>            <span class="n">updateSummaryAttributes</span><span class="o">(</span><span class="n">requestSummary</span><span class="o">,</span> <span class="n">summaryFromDb</span><span class="o">);</span>
</span><span class='line'>        <span class="o">}</span>
</span><span class='line'>        <span class="k">if</span> <span class="o">(</span><span class="n">updatedFields</span><span class="o">.</span><span class="na">containsKey</span><span class="o">(</span><span class="s">&quot;summary&quot;</span><span class="o">))</span> <span class="o">{</span>
</span><span class='line'>            <span class="n">customer</span><span class="o">.</span><span class="na">setSummary</span><span class="o">(</span><span class="n">summaryFromDb</span><span class="o">);</span>
</span><span class='line'>        <span class="o">}</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="kd">private</span> <span class="kt">void</span> <span class="nf">updateSummaryAttributes</span><span class="o">(</span><span class="n">RequestSummary</span> <span class="n">requestSummary</span><span class="o">,</span> <span class="n">Summary</span> <span class="n">dbSummary</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">String</span> <span class="n">requestSummaryDetail</span> <span class="o">=</span> <span class="n">requestSummary</span><span class="o">.</span><span class="na">getSummaryDetail</span><span class="o">();</span>
</span><span class='line'>        <span class="n">TrustIndicator</span> <span class="n">requestTrustIndicator</span> <span class="o">=</span> <span class="n">requestSummary</span><span class="o">.</span><span class="na">getTrustIndicator</span><span class="o">();</span>
</span><span class='line'>
</span><span class='line'>        <span class="n">String</span> <span class="n">dbSummaryDetail</span> <span class="o">=</span> <span class="n">dbSummary</span><span class="o">.</span><span class="na">getDetail</span><span class="o">();</span>
</span><span class='line'>        <span class="n">TrustIndicator</span> <span class="n">dbTrustIndicator</span> <span class="o">=</span> <span class="n">dbSummary</span><span class="o">.</span><span class="na">getTrustIndicator</span><span class="o">();</span>
</span><span class='line'>
</span><span class='line'>        <span class="n">UpdateAction</span> <span class="n">updateAction</span> <span class="o">=</span> <span class="n">getUpdateAction</span><span class="o">(</span><span class="n">requestSummaryDetail</span><span class="o">,</span>
</span><span class='line'>                <span class="n">dbSummaryDetail</span><span class="o">,</span> <span class="n">requestTrustIndicator</span><span class="o">,</span> <span class="n">dbTrustIndicator</span><span class="o">);</span>
</span><span class='line'>
</span><span class='line'>        <span class="k">if</span> <span class="o">(</span><span class="n">updateAction</span><span class="o">.</span><span class="na">updateDetail</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>            <span class="n">dbSummary</span><span class="o">.</span><span class="na">setDetail</span><span class="o">(</span><span class="n">trimToNull</span><span class="o">(</span><span class="n">requestSummaryDetail</span><span class="o">));</span>
</span><span class='line'>            <span class="n">updatedFields</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="s">&quot;summary&quot;</span><span class="o">,</span> <span class="n">dbSummary</span><span class="o">);</span>
</span><span class='line'>        <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>        <span class="n">dbSummary</span><span class="o">.</span><span class="na">setTrustIndicator</span><span class="o">(</span><span class="n">updateAction</span><span class="o">.</span><span class="na">updatedTrustIndicator</span><span class="o">);</span>
</span><span class='line'>
</span><span class='line'>        <span class="k">if</span> <span class="o">(</span><span class="n">updateAction</span><span class="o">.</span><span class="na">updatedTrustIndicator</span> <span class="o">!=</span> <span class="n">dbTrustIndicator</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>            <span class="n">updatedFields</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="s">&quot;summary&quot;</span><span class="o">,</span> <span class="n">dbSummary</span><span class="o">);</span>
</span><span class='line'>        <span class="o">}</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="kd">private</span> <span class="n">UpdateAction</span> <span class="nf">getUpdateAction</span><span class="o">(</span><span class="n">String</span> <span class="n">requestSummaryDetail</span><span class="o">,</span> <span class="n">String</span> <span class="n">dbSummaryDetail</span><span class="o">,</span>
</span><span class='line'>                                         <span class="n">TrustIndicator</span> <span class="n">requestTrustIndicator</span><span class="o">,</span>
</span><span class='line'>                                         <span class="n">TrustIndicator</span> <span class="n">dbTrustIndicator</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">UpdateAction</span> <span class="n">updateAction</span><span class="o">;</span>
</span><span class='line'>        <span class="kt">boolean</span> <span class="n">isRequestDetailBlank</span><span class="o">;</span>
</span><span class='line'>        <span class="kt">boolean</span> <span class="n">isDbDetailBlank</span><span class="o">;</span>
</span><span class='line'>        <span class="kt">boolean</span> <span class="n">isRequestAndDbDetailSame</span><span class="o">;</span>
</span><span class='line'>
</span><span class='line'>        <span class="n">updateAction</span> <span class="o">=</span> <span class="k">new</span> <span class="n">UpdateAction</span><span class="o">();</span>
</span><span class='line'>        <span class="n">updateAction</span><span class="o">.</span><span class="na">updatedTrustIndicator</span> <span class="o">=</span> <span class="n">dbTrustIndicator</span><span class="o">;</span>
</span><span class='line'>
</span><span class='line'>        <span class="k">if</span> <span class="o">(</span><span class="kc">null</span> <span class="o">==</span> <span class="n">dbSummaryDetail</span> <span class="o">||</span> <span class="s">&quot;&quot;</span><span class="o">.</span><span class="na">equals</span><span class="o">(</span><span class="n">dbSummaryDetail</span><span class="o">))</span> <span class="o">{</span>
</span><span class='line'>            <span class="n">isDbDetailBlank</span> <span class="o">=</span> <span class="kc">true</span><span class="o">;</span>
</span><span class='line'>        <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
</span><span class='line'>            <span class="n">isDbDetailBlank</span> <span class="o">=</span> <span class="kc">false</span><span class="o">;</span>
</span><span class='line'>        <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>        <span class="k">if</span> <span class="o">(</span><span class="kc">null</span> <span class="o">==</span> <span class="n">requestSummaryDetail</span> <span class="o">||</span> <span class="s">&quot;&quot;</span><span class="o">.</span><span class="na">equals</span><span class="o">(</span><span class="n">requestSummaryDetail</span><span class="o">))</span> <span class="o">{</span>
</span><span class='line'>            <span class="n">isRequestDetailBlank</span> <span class="o">=</span> <span class="kc">true</span><span class="o">;</span>
</span><span class='line'>        <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
</span><span class='line'>            <span class="n">isRequestDetailBlank</span> <span class="o">=</span> <span class="kc">false</span><span class="o">;</span>
</span><span class='line'>        <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>        <span class="k">if</span> <span class="o">(</span><span class="kc">null</span> <span class="o">!=</span> <span class="n">requestSummaryDetail</span> <span class="o">&amp;&amp;</span> <span class="kc">null</span> <span class="o">!=</span> <span class="n">dbSummaryDetail</span>
</span><span class='line'>                <span class="o">&amp;&amp;</span> <span class="n">requestSummaryDetail</span><span class="o">.</span><span class="na">equalsIgnoreCase</span><span class="o">(</span><span class="n">dbSummaryDetail</span><span class="o">))</span> <span class="o">{</span>
</span><span class='line'>            <span class="n">isRequestAndDbDetailSame</span> <span class="o">=</span> <span class="kc">true</span><span class="o">;</span>
</span><span class='line'>        <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
</span><span class='line'>            <span class="n">isRequestAndDbDetailSame</span> <span class="o">=</span> <span class="kc">false</span><span class="o">;</span>
</span><span class='line'>        <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>        <span class="k">if</span> <span class="o">(</span><span class="n">isRequestDetailBlank</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">isDbDetailBlank</span> <span class="o">&amp;&amp;</span> <span class="n">TRUSTED</span> <span class="o">==</span> <span class="n">requestTrustIndicator</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>            <span class="k">if</span> <span class="o">(</span><span class="n">TRUSTED</span> <span class="o">==</span> <span class="n">dbTrustIndicator</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>                <span class="n">updateAction</span><span class="o">.</span><span class="na">updateDetail</span> <span class="o">=</span> <span class="kc">true</span><span class="o">;</span>
</span><span class='line'>            <span class="o">}</span> <span class="k">else</span> <span class="k">if</span> <span class="o">(</span><span class="n">UNTRUSTED</span> <span class="o">==</span> <span class="n">dbTrustIndicator</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>                <span class="n">updateAction</span><span class="o">.</span><span class="na">updateDetail</span> <span class="o">=</span> <span class="kc">true</span><span class="o">;</span>
</span><span class='line'>                <span class="n">updateAction</span><span class="o">.</span><span class="na">updatedTrustIndicator</span> <span class="o">=</span> <span class="n">TRUSTED</span><span class="o">;</span>
</span><span class='line'>            <span class="o">}</span>
</span><span class='line'>        <span class="o">}</span> <span class="k">else</span> <span class="k">if</span> <span class="o">(!</span><span class="n">isRequestDetailBlank</span> <span class="o">&amp;&amp;</span> <span class="n">isDbDetailBlank</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>            <span class="n">updateAction</span><span class="o">.</span><span class="na">updateDetail</span> <span class="o">=</span> <span class="kc">true</span><span class="o">;</span>
</span><span class='line'>            <span class="n">updateAction</span><span class="o">.</span><span class="na">updatedTrustIndicator</span> <span class="o">=</span> <span class="n">requestTrustIndicator</span><span class="o">;</span>
</span><span class='line'>        <span class="o">}</span> <span class="k">else</span> <span class="k">if</span> <span class="o">(!</span><span class="n">isRequestDetailBlank</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">isDbDetailBlank</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>            <span class="k">if</span> <span class="o">(</span><span class="n">TRUSTED</span> <span class="o">==</span> <span class="n">dbTrustIndicator</span> <span class="o">&amp;&amp;</span> <span class="n">TRUSTED</span> <span class="o">==</span> <span class="n">requestTrustIndicator</span>
</span><span class='line'>                    <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">isRequestAndDbDetailSame</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>                <span class="n">updateAction</span><span class="o">.</span><span class="na">updateDetail</span> <span class="o">=</span> <span class="kc">true</span><span class="o">;</span>
</span><span class='line'>            <span class="o">}</span> <span class="k">else</span> <span class="k">if</span> <span class="o">(</span><span class="n">UNTRUSTED</span> <span class="o">==</span> <span class="n">dbTrustIndicator</span> <span class="o">&amp;&amp;</span> <span class="n">TRUSTED</span> <span class="o">==</span> <span class="n">requestTrustIndicator</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>                <span class="n">updateAction</span><span class="o">.</span><span class="na">updatedTrustIndicator</span> <span class="o">=</span> <span class="n">TRUSTED</span><span class="o">;</span>
</span><span class='line'>                <span class="k">if</span> <span class="o">(!</span><span class="n">isRequestAndDbDetailSame</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>                    <span class="n">updateAction</span><span class="o">.</span><span class="na">updateDetail</span> <span class="o">=</span> <span class="kc">true</span><span class="o">;</span>
</span><span class='line'>                <span class="o">}</span>
</span><span class='line'>            <span class="o">}</span> <span class="k">else</span> <span class="k">if</span> <span class="o">(</span><span class="n">UNTRUSTED</span> <span class="o">==</span> <span class="n">dbTrustIndicator</span> <span class="o">&amp;&amp;</span> <span class="n">UNTRUSTED</span> <span class="o">==</span> <span class="n">requestTrustIndicator</span>
</span><span class='line'>                    <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">isRequestAndDbDetailSame</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>                <span class="n">updateAction</span><span class="o">.</span><span class="na">updateDetail</span> <span class="o">=</span> <span class="kc">true</span><span class="o">;</span>
</span><span class='line'>            <span class="o">}</span> <span class="k">else</span> <span class="k">if</span> <span class="o">(</span><span class="n">TRUSTED</span> <span class="o">==</span> <span class="n">dbTrustIndicator</span> <span class="o">&amp;&amp;</span> <span class="n">UNTRUSTED</span> <span class="o">==</span> <span class="n">requestTrustIndicator</span>
</span><span class='line'>                    <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">isRequestAndDbDetailSame</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>                <span class="n">updateAction</span><span class="o">.</span><span class="na">updateDetail</span> <span class="o">=</span> <span class="kc">true</span><span class="o">;</span>
</span><span class='line'>                <span class="n">updateAction</span><span class="o">.</span><span class="na">updatedTrustIndicator</span> <span class="o">=</span> <span class="n">UNTRUSTED</span><span class="o">;</span>
</span><span class='line'>            <span class="o">}</span>
</span><span class='line'>        <span class="o">}</span>
</span><span class='line'>        <span class="k">return</span> <span class="n">updateAction</span><span class="o">;</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="kd">private</span> <span class="kd">static</span> <span class="n">String</span> <span class="nf">trimToNull</span><span class="o">(</span><span class="n">String</span> <span class="n">target</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="k">if</span> <span class="o">(</span><span class="n">target</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>            <span class="k">return</span> <span class="kc">null</span><span class="o">;</span>
</span><span class='line'>        <span class="o">}</span>
</span><span class='line'>        <span class="n">String</span> <span class="n">trimmed</span> <span class="o">=</span> <span class="n">target</span><span class="o">.</span><span class="na">trim</span><span class="o">();</span>
</span><span class='line'>        <span class="k">return</span> <span class="s">&quot;&quot;</span><span class="o">.</span><span class="na">equals</span><span class="o">(</span><span class="n">trimmed</span><span class="o">)</span> <span class="o">?</span> <span class="kc">null</span> <span class="o">:</span> <span class="n">trimmed</span><span class="o">;</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="kd">private</span> <span class="kd">class</span> <span class="nc">UpdateAction</span> <span class="o">{</span>
</span><span class='line'>        <span class="kd">public</span> <span class="kt">boolean</span> <span class="n">updateDetail</span><span class="o">;</span>
</span><span class='line'>        <span class="kd">public</span> <span class="n">TrustIndicator</span> <span class="n">updatedTrustIndicator</span><span class="o">;</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<h2>加测试</h2>

<p>我们用Spock写了一个很<em>性感</em>的测试：（实际情况是，用jUnit写出来的测试实在是看不懂，木有办法了才用这个。。。。。。）</p>

<figure class='code'><figcaption><span>SummaryUpdateHelperTest.groovy</span><a href='https://github.com/YaodanZhang/code-kata/blob/master/src/test/groovy/com/thoughtworks/kata/refactor/SummaryUpdateHelperTest.groovy'>link</a></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="kd">class</span> <span class="nc">SummaryUpdateHelperTest</span> <span class="kd">extends</span> <span class="n">Specification</span> <span class="o">{</span>
</span><span class='line'>
</span><span class='line'>    <span class="nd">@Unroll</span>
</span><span class='line'>    <span class="kt">def</span> <span class="s2">&quot;request[#requestSummary, #requestTrust] update db[#dbSummary, #dbTrust] = [#finalSummary, #finalTrust]&quot;</span><span class="o">()</span> <span class="o">{</span>
</span><span class='line'>        <span class="nl">given:</span>
</span><span class='line'>        <span class="kt">def</span> <span class="n">helper</span> <span class="o">=</span> <span class="k">new</span> <span class="n">SummaryUpdateHelper</span><span class="o">(</span><span class="n">newHashMap</span><span class="o">())</span>
</span><span class='line'>        <span class="kt">def</span> <span class="n">request</span> <span class="o">=</span> <span class="n">createRequest</span><span class="o">(</span><span class="n">requestSummary</span><span class="o">,</span> <span class="n">requestTrust</span><span class="o">)</span>
</span><span class='line'>        <span class="kt">def</span> <span class="n">customer</span> <span class="o">=</span> <span class="n">createCustomer</span><span class="o">(</span><span class="n">dbSummary</span><span class="o">,</span> <span class="n">dbTrust</span><span class="o">)</span>
</span><span class='line'>
</span><span class='line'>        <span class="nl">when:</span>
</span><span class='line'>        <span class="n">helper</span><span class="o">.</span><span class="na">updateSummary</span><span class="o">(</span><span class="n">request</span><span class="o">,</span> <span class="n">customer</span><span class="o">)</span>
</span><span class='line'>
</span><span class='line'>        <span class="nl">then:</span>
</span><span class='line'>        <span class="n">customer</span><span class="o">.</span><span class="na">summary</span> <span class="o">==</span> <span class="n">createSummary</span><span class="o">(</span><span class="n">finalSummary</span><span class="o">,</span> <span class="n">finalTrust</span><span class="o">)</span>
</span><span class='line'>
</span><span class='line'>        <span class="nl">where:</span>
</span><span class='line'>        <span class="n">requestSummary</span> <span class="o">|</span> <span class="n">requestTrust</span> <span class="o">|</span> <span class="n">dbSummary</span> <span class="o">|</span> <span class="n">dbTrust</span>   <span class="o">|</span> <span class="n">finalSummary</span> <span class="o">|</span> <span class="n">finalTrust</span>
</span><span class='line'>        <span class="kc">null</span>           <span class="o">|</span> <span class="kc">null</span>         <span class="o">|</span> <span class="kc">null</span>      <span class="o">|</span> <span class="kc">null</span>      <span class="o">|</span> <span class="kc">null</span>         <span class="o">|</span> <span class="kc">null</span>
</span><span class='line'>
</span><span class='line'>        <span class="n">BLANK</span>          <span class="o">|</span> <span class="n">TRUSTED</span>      <span class="o">|</span> <span class="kc">null</span>      <span class="o">|</span> <span class="kc">null</span>      <span class="o">|</span> <span class="kc">null</span>         <span class="o">|</span> <span class="kc">null</span>
</span><span class='line'>
</span><span class='line'>        <span class="cm">/********************************这里省掉58个测试用例*********************************/</span>
</span><span class='line'>
</span><span class='line'>        <span class="n">UPDATED</span>        <span class="o">|</span> <span class="n">UNTRUSTED</span>    <span class="o">|</span> <span class="n">ORIGINAL</span>  <span class="o">|</span> <span class="n">UNTRUSTED</span> <span class="o">|</span> <span class="n">UPDATED</span>      <span class="o">|</span> <span class="n">UNTRUSTED</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="cm">/*************************************下面的不重要**************************************/</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<h2>重构</h2>

<p>接下来，就是要做重构了。</p>

<p>在第一段代码中，我们一眼就能看到，这里面有一个很销魂的方法：<code>getUpdateAction()</code>。别的就略过啦，今天我们就来看看如何重构这个方法。</p>

<h3>1. 干掉里面最二的代码</h3>

<p>像下面这个：</p>

<figure class='code'><figcaption><span>SummaryUpdateHelper.java</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'><span class="k">if</span> <span class="o">(</span><span class="kc">null</span> <span class="o">==</span> <span class="n">dbSummaryDetail</span> <span class="o">||</span> <span class="s">&quot;&quot;</span><span class="o">.</span><span class="na">equals</span><span class="o">(</span><span class="n">dbSummaryDetail</span><span class="o">))</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">isDbDetailBlank</span> <span class="o">=</span> <span class="kc">true</span><span class="o">;</span>
</span><span class='line'><span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">isDbDetailBlank</span> <span class="o">=</span> <span class="kc">false</span><span class="o">;</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>一行代码就搞定了：<code>isDbDetailBlank = isNullOrEmpty(dbSummaryDetail);</code></p>

<p>简单来说，这一步完成过后，IDE里面就不会出现警告了。</p>

<h3>2. 分离关注点</h3>

<p>现在就需要动动脑筋了，首先我们可以看到，在方法<code>getUpdateAction()</code>中，他是把判断是否更新<em>Detail</em>和<em>Indicator</em>搅在一起了，想一次解决两个问题。但奇怪的是对于<em>Detail</em>，他返回的是是否需要更新；对于<em>Indicator</em>，他返回的是更新后的值。。。。。</p>

<p>我们可以首先把<em>Detail</em>和<em>Indicator</em>的判断分开。分开过后，我们就可以发现，以前专门用来保存<em>Detail</em>和<em>Indicator</em>的类<code>UpdateAction</code>就没有用了。我们先来看看抽出来后单独判断更新后的<em>Detail</em>的方法，关于<em>Indicator</em>的判断大家自己脑补。</p>

<figure class='code'><figcaption><span>SummaryUpdateHelper.java</span><a href='https://github.com/YaodanZhang/code-kata/blob/master/src/main/java/com/thoughtworks/kata/refactor/SummaryUpdateHelper.java'>link</a></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'><span class="kd">private</span> <span class="kt">boolean</span> <span class="nf">shouldUpdateDetail</span><span class="o">(</span><span class="n">String</span> <span class="n">requestSummaryDetail</span><span class="o">,</span> <span class="n">String</span> <span class="n">dbSummaryDetail</span><span class="o">,</span>
</span><span class='line'>                                   <span class="n">TrustIndicator</span> <span class="n">requestTrustIndicator</span><span class="o">,</span>
</span><span class='line'>                                   <span class="n">TrustIndicator</span> <span class="n">dbTrustIndicator</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>    <span class="kt">boolean</span> <span class="n">isDbDetailBlank</span> <span class="o">=</span> <span class="n">isNullOrEmpty</span><span class="o">(</span><span class="n">dbSummaryDetail</span><span class="o">);</span>
</span><span class='line'>    <span class="kt">boolean</span> <span class="n">isRequestDetailBlank</span> <span class="o">=</span> <span class="n">isNullOrEmpty</span><span class="o">(</span><span class="n">requestSummaryDetail</span><span class="o">);</span>
</span><span class='line'>    <span class="kt">boolean</span> <span class="n">isRequestAndDbDetailSame</span> <span class="o">=</span> <span class="n">isSame</span><span class="o">(</span><span class="n">requestSummaryDetail</span><span class="o">,</span> <span class="n">dbSummaryDetail</span><span class="o">);</span>
</span><span class='line'>
</span><span class='line'>    <span class="kt">boolean</span> <span class="n">shouldUpdateDetail</span> <span class="o">=</span> <span class="kc">false</span><span class="o">;</span>
</span><span class='line'>    <span class="k">if</span> <span class="o">(</span><span class="n">isRequestDetailBlank</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">isDbDetailBlank</span> <span class="o">&amp;&amp;</span> <span class="n">TRUSTED</span> <span class="o">==</span> <span class="n">requestTrustIndicator</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="k">if</span> <span class="o">(</span><span class="n">TRUSTED</span> <span class="o">==</span> <span class="n">dbTrustIndicator</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>            <span class="n">shouldUpdateDetail</span> <span class="o">=</span> <span class="kc">true</span><span class="o">;</span>
</span><span class='line'>        <span class="o">}</span> <span class="k">else</span> <span class="k">if</span> <span class="o">(</span><span class="n">UNTRUSTED</span> <span class="o">==</span> <span class="n">dbTrustIndicator</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>            <span class="n">shouldUpdateDetail</span> <span class="o">=</span> <span class="kc">true</span><span class="o">;</span>
</span><span class='line'>        <span class="o">}</span>
</span><span class='line'>    <span class="o">}</span> <span class="k">else</span> <span class="k">if</span> <span class="o">(!</span><span class="n">isRequestDetailBlank</span> <span class="o">&amp;&amp;</span> <span class="n">isDbDetailBlank</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">shouldUpdateDetail</span> <span class="o">=</span> <span class="kc">true</span><span class="o">;</span>
</span><span class='line'>    <span class="o">}</span> <span class="k">else</span> <span class="k">if</span> <span class="o">(!</span><span class="n">isRequestDetailBlank</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="k">if</span> <span class="o">(</span><span class="n">TRUSTED</span> <span class="o">==</span> <span class="n">dbTrustIndicator</span> <span class="o">&amp;&amp;</span> <span class="n">TRUSTED</span> <span class="o">==</span> <span class="n">requestTrustIndicator</span>
</span><span class='line'>                <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">isRequestAndDbDetailSame</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>            <span class="n">shouldUpdateDetail</span> <span class="o">=</span> <span class="kc">true</span><span class="o">;</span>
</span><span class='line'>        <span class="o">}</span> <span class="k">else</span> <span class="k">if</span> <span class="o">(</span><span class="n">UNTRUSTED</span> <span class="o">==</span> <span class="n">dbTrustIndicator</span> <span class="o">&amp;&amp;</span> <span class="n">TRUSTED</span> <span class="o">==</span> <span class="n">requestTrustIndicator</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>            <span class="k">if</span> <span class="o">(!</span><span class="n">isRequestAndDbDetailSame</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>                <span class="n">shouldUpdateDetail</span> <span class="o">=</span> <span class="kc">true</span><span class="o">;</span>
</span><span class='line'>            <span class="o">}</span>
</span><span class='line'>        <span class="o">}</span> <span class="k">else</span> <span class="k">if</span> <span class="o">(</span><span class="n">UNTRUSTED</span> <span class="o">==</span> <span class="n">dbTrustIndicator</span> <span class="o">&amp;&amp;</span> <span class="n">UNTRUSTED</span> <span class="o">==</span> <span class="n">requestTrustIndicator</span>
</span><span class='line'>                <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">isRequestAndDbDetailSame</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>            <span class="n">shouldUpdateDetail</span> <span class="o">=</span> <span class="kc">true</span><span class="o">;</span>
</span><span class='line'>        <span class="o">}</span> <span class="k">else</span> <span class="k">if</span> <span class="o">(</span><span class="n">TRUSTED</span> <span class="o">==</span> <span class="n">dbTrustIndicator</span> <span class="o">&amp;&amp;</span> <span class="n">UNTRUSTED</span> <span class="o">==</span> <span class="n">requestTrustIndicator</span>
</span><span class='line'>                <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">isRequestAndDbDetailSame</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>            <span class="n">shouldUpdateDetail</span> <span class="o">=</span> <span class="kc">true</span><span class="o">;</span>
</span><span class='line'>        <span class="o">}</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>    <span class="k">return</span> <span class="n">shouldUpdateDetail</span><span class="o">;</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<h3>3. 重新理一下判断条件</h3>

<p>从上面这个方法我们可以看到，他的逻辑判断也是很<em>二</em>的，很多分支可以合并或者简化，这一步就做这个事情。具体的过程就不一步一步的演示了，有兴趣的童鞋可以去看看我<em>Github</em>的<a href="https://github.com/YaodanZhang/code-kata/tree/master/src/main/java/com/thoughtworks/kata/refactor">提交记录</a>。</p>

<figure class='code'><figcaption><span>SummaryUpdateHelper.java</span><a href='https://github.com/YaodanZhang/code-kata/blob/master/src/main/java/com/thoughtworks/kata/refactor/SummaryUpdateHelper.java'>link</a></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'><span class="kd">private</span> <span class="kt">boolean</span> <span class="nf">shouldUpdateDetail</span><span class="o">(</span><span class="n">String</span> <span class="n">requestSummaryDetail</span><span class="o">,</span> <span class="n">String</span> <span class="n">dbSummaryDetail</span><span class="o">,</span>
</span><span class='line'>                                   <span class="n">TrustIndicator</span> <span class="n">requestTrustIndicator</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>    <span class="kt">boolean</span> <span class="n">isDbDetailBlank</span> <span class="o">=</span> <span class="n">isNullOrEmpty</span><span class="o">(</span><span class="n">dbSummaryDetail</span><span class="o">);</span>
</span><span class='line'>
</span><span class='line'>    <span class="k">if</span> <span class="o">(</span><span class="n">isNullOrEmpty</span><span class="o">(</span><span class="n">requestSummaryDetail</span><span class="o">))</span> <span class="o">{</span>
</span><span class='line'>        <span class="k">return</span> <span class="o">!</span><span class="n">isDbDetailBlank</span> <span class="o">&amp;&amp;</span> <span class="n">TRUSTED</span> <span class="o">==</span> <span class="n">requestTrustIndicator</span><span class="o">;</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="k">return</span> <span class="n">isDbDetailBlank</span> <span class="o">||</span> <span class="o">!</span><span class="n">isSame</span><span class="o">(</span><span class="n">requestSummaryDetail</span><span class="o">,</span> <span class="n">dbSummaryDetail</span><span class="o">);</span>
</span><span class='line'><span class="o">}</span>
</span><span class='line'>
</span><span class='line'><span class="kd">private</span> <span class="n">TrustIndicator</span> <span class="nf">getUpdatedIndicator</span><span class="o">(</span><span class="n">String</span> <span class="n">requestSummaryDetail</span><span class="o">,</span> <span class="n">String</span> <span class="n">dbSummaryDetail</span><span class="o">,</span>
</span><span class='line'>                                                <span class="n">TrustIndicator</span> <span class="n">requestTrustIndicator</span><span class="o">,</span>
</span><span class='line'>                                                <span class="n">TrustIndicator</span> <span class="n">dbTrustIndicator</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">TrustIndicator</span> <span class="n">updatedIndicator</span> <span class="o">=</span> <span class="n">dbTrustIndicator</span><span class="o">;</span>
</span><span class='line'>
</span><span class='line'>    <span class="kt">boolean</span> <span class="n">isDbDetailBlank</span> <span class="o">=</span> <span class="n">isNullOrEmpty</span><span class="o">(</span><span class="n">dbSummaryDetail</span><span class="o">);</span>
</span><span class='line'>    <span class="kt">boolean</span> <span class="n">isRequestDetailBlank</span> <span class="o">=</span> <span class="n">isNullOrEmpty</span><span class="o">(</span><span class="n">requestSummaryDetail</span><span class="o">);</span>
</span><span class='line'>
</span><span class='line'>    <span class="k">if</span> <span class="o">(</span><span class="n">isRequestDetailBlank</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">isDbDetailBlank</span> <span class="o">&amp;&amp;</span> <span class="n">TRUSTED</span> <span class="o">==</span> <span class="n">requestTrustIndicator</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">updatedIndicator</span> <span class="o">=</span> <span class="n">requestTrustIndicator</span><span class="o">;</span>
</span><span class='line'>    <span class="o">}</span> <span class="k">else</span> <span class="k">if</span> <span class="o">(!</span><span class="n">isRequestDetailBlank</span> <span class="o">&amp;&amp;</span> <span class="n">isDbDetailBlank</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">updatedIndicator</span> <span class="o">=</span> <span class="n">requestTrustIndicator</span><span class="o">;</span>
</span><span class='line'>    <span class="o">}</span> <span class="k">else</span> <span class="k">if</span> <span class="o">(!</span><span class="n">isRequestDetailBlank</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="k">if</span> <span class="o">(</span><span class="n">TRUSTED</span> <span class="o">==</span> <span class="n">requestTrustIndicator</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>            <span class="n">updatedIndicator</span> <span class="o">=</span> <span class="n">requestTrustIndicator</span><span class="o">;</span>
</span><span class='line'>        <span class="o">}</span> <span class="k">else</span> <span class="k">if</span> <span class="o">(</span><span class="n">UNTRUSTED</span> <span class="o">==</span> <span class="n">requestTrustIndicator</span>
</span><span class='line'>                <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">isSame</span><span class="o">(</span><span class="n">requestSummaryDetail</span><span class="o">,</span> <span class="n">dbSummaryDetail</span><span class="o">))</span> <span class="o">{</span>
</span><span class='line'>            <span class="n">updatedIndicator</span> <span class="o">=</span> <span class="n">requestTrustIndicator</span><span class="o">;</span>
</span><span class='line'>        <span class="o">}</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>    <span class="k">return</span> <span class="n">updatedIndicator</span><span class="o">;</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>有没有感觉瞬间清爽了不少？</p>

<h3>4. 换一种思路去处理更新请求</h3>

<p>我们看看这两个方法，可以发现他们其中的判断条件是很相似的。我们再考虑一下这两个方法的业务场景，无非就是判断是否要更新<em>Summary</em>，而如果<em>Summary</em>的<em>Detail</em>或者<em>Indicator</em>其中的任何一个更新了，另外一个最终的值肯定也是会被设为跟<em>Request</em>中的值一样的（如果<em>Request</em>中的值跟数据库中的一样，我们可以视为更新了，也可以视为没有更新）。所以我们换一种思路，不去判断是否需要更改，我们只判断最终，该请求执行完成后，<em>Summary</em>的值是否更请求的值一样即可。</p>

<p>按照这个思路去重构，首先可以把<code>shouldUpdateDetail()</code>这个方法的行为给改掉，然后再改掉<code>getUpdatedIndicator()</code>的行为，让他们俩行为一致，最终用一个新的方法<code>shouldUpdate()</code>。下面就是最终的方法，具体的重构过程大家有兴趣可以去看我<em>Github</em>的<a href="https://github.com/YaodanZhang/code-kata/tree/master/src/main/java/com/thoughtworks/kata/refactor">提交记录</a>。</p>

<figure class='code'><figcaption><span>SummaryUpdateHelper.java</span><a href='https://github.com/YaodanZhang/code-kata/blob/master/src/main/java/com/thoughtworks/kata/refactor/SummaryUpdateHelper.java'>link</a></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'><span class="kd">private</span> <span class="kt">boolean</span> <span class="nf">shouldUpdate</span><span class="o">(</span><span class="n">String</span> <span class="n">requestSummaryDetail</span><span class="o">,</span> <span class="n">String</span> <span class="n">dbSummaryDetail</span><span class="o">,</span>
</span><span class='line'>                             <span class="n">TrustIndicator</span> <span class="n">requestTrustIndicator</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>    <span class="kt">boolean</span> <span class="n">isDbDetailBlank</span> <span class="o">=</span> <span class="n">isNullOrEmpty</span><span class="o">(</span><span class="n">dbSummaryDetail</span><span class="o">);</span>
</span><span class='line'>    <span class="kt">boolean</span> <span class="n">isRequestDetailBlank</span> <span class="o">=</span> <span class="n">isNullOrEmpty</span><span class="o">(</span><span class="n">requestSummaryDetail</span><span class="o">);</span>
</span><span class='line'>
</span><span class='line'>    <span class="k">if</span> <span class="o">(</span><span class="n">isRequestDetailBlank</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="k">return</span> <span class="o">!</span><span class="n">isDbDetailBlank</span> <span class="o">&amp;&amp;</span> <span class="n">TRUSTED</span> <span class="o">==</span> <span class="n">requestTrustIndicator</span><span class="o">;</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="k">return</span> <span class="n">isDbDetailBlank</span> <span class="o">||</span> <span class="n">shouldUpdateWhenBothNotBlank</span><span class="o">(</span><span class="n">requestSummaryDetail</span><span class="o">,</span>
</span><span class='line'>            <span class="n">dbSummaryDetail</span><span class="o">,</span> <span class="n">requestTrustIndicator</span><span class="o">);</span>
</span><span class='line'><span class="o">}</span>
</span><span class='line'>
</span><span class='line'><span class="kd">private</span> <span class="kt">boolean</span> <span class="nf">shouldUpdateWhenBothNotBlank</span><span class="o">(</span><span class="n">String</span> <span class="n">requestSummaryDetail</span><span class="o">,</span>
</span><span class='line'>                                             <span class="n">String</span> <span class="n">dbSummaryDetail</span><span class="o">,</span>
</span><span class='line'>                                             <span class="n">TrustIndicator</span> <span class="n">requestTrustIndicator</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>    <span class="k">return</span> <span class="n">TRUSTED</span> <span class="o">==</span> <span class="n">requestTrustIndicator</span>
</span><span class='line'>            <span class="o">||</span> <span class="n">UNTRUSTED</span> <span class="o">==</span> <span class="n">requestTrustIndicator</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">isSame</span><span class="o">(</span><span class="n">requestSummaryDetail</span><span class="o">,</span> <span class="n">dbSummaryDetail</span><span class="o">);</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>到这里，能做的重构基本上就完成了，基本达到了能让一个健康人理解的程度（吧？）。</p>

<h2>改掉这个Bug</h2>

<h3>1. 改测试</h3>

<p>改Bug的第一步当然是改测试，把现在的测试用例中与新需求的部分改掉，也就帮我们明确了修改目标了。</p>

<p>回顾一下，我们新的需求是神马？</p>

<blockquote><p>不要让untrusted的信息覆盖trusted</p></blockquote>

<p>那么，我们就根据这个来修改我们的测试用例，发现下面的这些测试用例是需要修改的：</p>

<figure class='code'><figcaption><span>SummaryUpdateHelperTest.groovy</span><a href='https://github.com/YaodanZhang/code-kata/blob/master/src/test/groovy/com/thoughtworks/kata/refactor/SummaryUpdateHelperTest.groovy'>link</a></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="o">...</span>
</span><span class='line'><span class="nl">where:</span>
</span><span class='line'><span class="n">requestSummary</span> <span class="o">|</span> <span class="n">requestTrust</span> <span class="o">|</span> <span class="n">dbSummary</span> <span class="o">|</span> <span class="n">dbTrust</span>   <span class="o">|</span> <span class="n">finalSummary</span> <span class="o">|</span> <span class="n">finalTrust</span>
</span><span class='line'><span class="n">BLANK</span>          <span class="o">|</span> <span class="n">TRUSTED</span>      <span class="o">|</span> <span class="kc">null</span>      <span class="o">|</span> <span class="kc">null</span>      <span class="o">|</span> <span class="n">BLANK</span>        <span class="o">|</span> <span class="n">TRUSTED</span>
</span><span class='line'><span class="n">BLANK</span>          <span class="o">|</span> <span class="n">TRUSTED</span>      <span class="o">|</span> <span class="n">BLANK</span>     <span class="o">|</span> <span class="n">UNTRUSTED</span> <span class="o">|</span> <span class="n">BLANK</span>        <span class="o">|</span> <span class="n">TRUSTED</span>
</span><span class='line'><span class="n">BLANK</span>          <span class="o">|</span> <span class="n">TRUSTED</span>      <span class="o">|</span> <span class="n">EMPTY</span>     <span class="o">|</span> <span class="n">UNTRUSTED</span> <span class="o">|</span> <span class="n">EMPTY</span>        <span class="o">|</span> <span class="n">TRUSTED</span>
</span><span class='line'>
</span><span class='line'><span class="n">EMPTY</span>          <span class="o">|</span> <span class="n">TRUSTED</span>      <span class="o">|</span> <span class="kc">null</span>      <span class="o">|</span> <span class="kc">null</span>      <span class="o">|</span> <span class="n">BLANK</span>        <span class="o">|</span> <span class="n">TRUSTED</span>
</span><span class='line'><span class="n">EMPTY</span>          <span class="o">|</span> <span class="n">TRUSTED</span>      <span class="o">|</span> <span class="n">BLANK</span>     <span class="o">|</span> <span class="n">UNTRUSTED</span> <span class="o">|</span> <span class="n">BLANK</span>        <span class="o">|</span> <span class="n">TRUSTED</span>
</span><span class='line'><span class="n">EMPTY</span>          <span class="o">|</span> <span class="n">TRUSTED</span>      <span class="o">|</span> <span class="n">EMPTY</span>     <span class="o">|</span> <span class="n">UNTRUSTED</span> <span class="o">|</span> <span class="n">EMPTY</span>        <span class="o">|</span> <span class="n">TRUSTED</span>
</span><span class='line'>
</span><span class='line'><span class="n">ORIGINAL</span>       <span class="o">|</span> <span class="n">UNTRUSTED</span>    <span class="o">|</span> <span class="n">BLANK</span>     <span class="o">|</span> <span class="n">TRUSTED</span>   <span class="o">|</span> <span class="n">BLANK</span>        <span class="o">|</span> <span class="n">TRUSTED</span>
</span><span class='line'><span class="n">ORIGINAL</span>       <span class="o">|</span> <span class="n">UNTRUSTED</span>    <span class="o">|</span> <span class="n">EMPTY</span>     <span class="o">|</span> <span class="n">TRUSTED</span>   <span class="o">|</span> <span class="n">EMPTY</span>        <span class="o">|</span> <span class="n">TRUSTED</span>
</span><span class='line'>
</span><span class='line'><span class="n">UPDATED</span>        <span class="o">|</span> <span class="n">UNTRUSTED</span>    <span class="o">|</span> <span class="n">BLANK</span>     <span class="o">|</span> <span class="n">TRUSTED</span>   <span class="o">|</span> <span class="n">BLANK</span>        <span class="o">|</span> <span class="n">TRUSTED</span>
</span><span class='line'><span class="n">UPDATED</span>        <span class="o">|</span> <span class="n">UNTRUSTED</span>    <span class="o">|</span> <span class="n">EMPTY</span>     <span class="o">|</span> <span class="n">TRUSTED</span>   <span class="o">|</span> <span class="n">EMPTY</span>        <span class="o">|</span> <span class="n">TRUSTED</span>
</span><span class='line'><span class="n">UPDATED</span>        <span class="o">|</span> <span class="n">UNTRUSTED</span>    <span class="o">|</span> <span class="n">ORIGINAL</span>  <span class="o">|</span> <span class="n">TRUSTED</span>   <span class="o">|</span> <span class="n">ORIGINAL</span>     <span class="o">|</span> <span class="n">TRUSTED</span>
</span><span class='line'><span class="o">...</span>
</span></code></pre></td></tr></table></div></figure>


<h3>2. 改实现代码</h3>

<p>这里就是要改一个方法：<code>shouldUpdate()</code>。当<em>Request</em>中的值为空时，我们仅在其<em>Indicator</em>为<em>trusted</em>的时候更新数据库的值；否则，只要不是数据库为<em>trusted</em>且<em>Request</em>为<em>untrusted</em>这种情况，我们都应该更新数据库的值。有了这个理解，代码改起来就简单了：</p>

<figure class='code'><figcaption><span>SummaryUpdateHelper.java</span><a href='https://github.com/YaodanZhang/code-kata/blob/master/src/main/java/com/thoughtworks/kata/refactor/SummaryUpdateHelper.java'>link</a></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'><span class="kd">private</span> <span class="kt">boolean</span> <span class="nf">shouldUpdate</span><span class="o">(</span><span class="n">String</span> <span class="n">requestSummaryDetail</span><span class="o">,</span> <span class="n">TrustIndicator</span> <span class="n">requestTrustIndicator</span><span class="o">,</span>
</span><span class='line'>                             <span class="n">TrustIndicator</span> <span class="n">dbTrustIndicator</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>    <span class="k">if</span> <span class="o">(</span><span class="n">isNullOrEmpty</span><span class="o">(</span><span class="n">requestSummaryDetail</span><span class="o">))</span> <span class="o">{</span>
</span><span class='line'>        <span class="k">return</span> <span class="n">TRUSTED</span> <span class="o">==</span> <span class="n">requestTrustIndicator</span><span class="o">;</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>    <span class="k">return</span> <span class="o">!(</span><span class="n">TRUSTED</span> <span class="o">==</span> <span class="n">dbTrustIndicator</span> <span class="o">&amp;&amp;</span> <span class="n">UNTRUSTED</span> <span class="o">==</span> <span class="n">requestTrustIndicator</span><span class="o">);</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>这就是最终的结果，大家和之前的代码对比一下，是不是成功将一个50行的if-else块重构到了3行（除了那个无意义的反大括号）。</p>

<h2>事后</h2>

<p>这个一万行的类确实是真实存在于我们一个遗留系统中的，每次新的需求过来，如果涉及到要修改这个类，大家都分外的小心翼翼，因为没有单元测试，功能测试也没法覆盖所有的场景，导致每一行代码的修改造成的风险都是很高的。</p>

<p>渐渐的，在一次又一次的被坑了过后，我们总结出了一套修改这类代码的方法，这就是上面提到的步骤：</p>

<ol>
<li>找到需要修改的部分，把这个部分抽取出来</li>
<li>根据业务场景和现有代码逻辑给这部分代码加测试（单元测试，端到端的功能测试等）</li>
<li>重构</li>
<li>修改</li>
<li>重构</li>
<li>&hellip;</li>
</ol>


<p>改完这个，心情大好，想想，要是如果每天都能重构500行，那么这个一万行的类还是。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。算了吧，能不改就别改了！</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Gradle学习笔记-1：基础知识]]></title>
    <link href="http://yaodanzhang.com/blog/2013/08/20/notes-of-gradle-serials-1/"/>
    <updated>2013-08-20T09:56:00+08:00</updated>
    <id>http://yaodanzhang.com/blog/2013/08/20/notes-of-gradle-serials-1</id>
    <content type="html"><![CDATA[<h2>Hello World</h2>

<p>在Gradle中，每一个build都由一个或多个project组成，而每一个project都由一个或多个task组成。</p>

<p>这是一个task的Hello World：</p>

<figure class='code'><figcaption><span>build.gradle </span></figcaption>
<div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="n">task</span> <span class="n">hello</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">doLast</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">println</span> <span class="s1">&#39;Hello World&#39;</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>




<!--more -->


<p>当执行命令：<code>gradle -q hello</code>时输出：</p>

<figure class='code'><figcaption><span>gradle -q hello </span></figcaption>
<div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>Hello World</span></code></pre></td></tr></table></div></figure>


<p>至于这个参数<code>-q</code>是什么意思，我们可以试试不加参数时的输出：</p>

<figure class='code'><figcaption><span>gradle hello </span></figcaption>
<div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>:hello
</span><span class='line'>Hello World
</span><span class='line'>
</span><span class='line'>BUILD SUCCESSFUL
</span><span class='line'>
</span><span class='line'>Total time: 1.879 secs</span></code></pre></td></tr></table></div></figure>


<p>可以看到，<code>-q</code>的意思不显示Gradle本身的log。</p>

<p>对于上面的<code>doLast</code>块，有一种简写的方式：</p>

<figure class='code'><figcaption><span>build.gradle </span></figcaption>
<div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="n">task</span> <span class="n">hello</span> <span class="o">&lt;&lt;</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">println</span> <span class="s1">&#39;Hello World&#39;</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<h2>使用Groovy</h2>

<p>在Gradle脚本中可以使用Groovy的语法，如下：</p>

<figure class='code'><figcaption><span>build.gradle </span></figcaption>
<div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="n">task</span> <span class="n">upper</span> <span class="o">&lt;&lt;</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">String</span> <span class="n">someString</span> <span class="o">=</span> <span class="s1">&#39;mY_nAmE&#39;</span>
</span><span class='line'>    <span class="n">println</span> <span class="s2">&quot;Original: &quot;</span> <span class="o">+</span> <span class="n">someString</span>
</span><span class='line'>    <span class="n">println</span> <span class="s2">&quot;Upper case: &quot;</span> <span class="o">+</span> <span class="n">someString</span><span class="o">.</span><span class="na">toUpperCase</span><span class="o">()</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>当执行<code>gradle -q upper</code>时，输出如下：</p>

<figure class='code'><figcaption><span>gradle -q upper </span></figcaption>
<div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>Original: mY_nAmE
</span><span class='line'>Upper case: MY_NAME</span></code></pre></td></tr></table></div></figure>


<h2>Task之间的依赖</h2>

<figure class='code'><figcaption><span>build.gradle </span></figcaption>
<div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="n">task</span> <span class="n">hello</span> <span class="o">&lt;&lt;</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">println</span> <span class="s1">&#39;Hello world!&#39;</span>
</span><span class='line'><span class="o">}</span>
</span><span class='line'>
</span><span class='line'><span class="n">task</span> <span class="nf">intro</span><span class="o">(</span><span class="nl">dependsOn:</span> <span class="n">hello</span><span class="o">)</span> <span class="o">&lt;&lt;</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">println</span> <span class="s2">&quot;I&#39;m Gradle&quot;</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>执行结果如下：</p>

<figure class='code'><figcaption><span>gradle -q intro </span></figcaption>
<div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>Hello World
</span><span class='line'>I'm Gradle</span></code></pre></td></tr></table></div></figure>


<h2>动态Task</h2>

<p>在Gradle里面，Task是可以动态定义的，如下：</p>

<figure class='code'><figcaption><span>build.gradle </span></figcaption>
<div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="mi">4</span><span class="o">.</span><span class="na">times</span> <span class="o">{</span> <span class="n">counter</span> <span class="o">-&gt;</span>
</span><span class='line'>    <span class="n">task</span> <span class="s2">&quot;task$counter&quot;</span><span class="o">(</span><span class="nl">dependsOn:</span> <span class="s2">&quot;depending$counter&quot;</span><span class="o">)</span> <span class="o">&lt;&lt;</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">println</span> <span class="s2">&quot;I&#39;m task number $counter&quot;</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'><span class="o">}</span>
</span><span class='line'>
</span><span class='line'><span class="mi">4</span><span class="o">.</span><span class="na">times</span> <span class="o">{</span> <span class="n">counter</span> <span class="o">-&gt;</span>
</span><span class='line'>    <span class="n">task</span> <span class="s2">&quot;depending$counter&quot;</span> <span class="o">&lt;&lt;</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">println</span> <span class="s2">&quot;I&#39;m depending number $counter&quot;</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>执行结果如下：</p>

<figure class='code'><figcaption><span>gradle -q task1 </span></figcaption>
<div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>I'm depending number 1
</span><span class='line'>I'm task number 1</span></code></pre></td></tr></table></div></figure>


<h2>多次定义Task的依赖</h2>

<figure class='code'><figcaption><span>build.gradle </span></figcaption>
<div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="mi">4</span><span class="o">.</span><span class="na">times</span> <span class="o">{</span> <span class="n">counter</span> <span class="o">-&gt;</span>
</span><span class='line'>    <span class="n">task</span> <span class="s2">&quot;task$counter&quot;</span><span class="o">(</span><span class="nl">dependsOn:</span> <span class="s2">&quot;depending$counter&quot;</span><span class="o">)</span> <span class="o">&lt;&lt;</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">println</span> <span class="s2">&quot;I&#39;m task number $counter&quot;</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'><span class="o">}</span>
</span><span class='line'>
</span><span class='line'><span class="n">task0</span><span class="o">.</span><span class="na">dependsOn</span> <span class="n">task1</span><span class="o">,</span> <span class="n">task2</span>
</span><span class='line'>
</span><span class='line'><span class="mi">4</span><span class="o">.</span><span class="na">times</span> <span class="o">{</span> <span class="n">counter</span> <span class="o">-&gt;</span>
</span><span class='line'>    <span class="n">task</span> <span class="s2">&quot;depending$counter&quot;</span> <span class="o">&lt;&lt;</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">println</span> <span class="s2">&quot;I&#39;m depending number $counter&quot;</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>执行结果如下：</p>

<figure class='code'><figcaption><span>gradle -q task0 </span></figcaption>
<div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>I'm depending number 0
</span><span class='line'>I'm depending number 1
</span><span class='line'>I'm task number 1
</span><span class='line'>I'm depending number 2
</span><span class='line'>I'm task number 2
</span><span class='line'>I'm task number 0</span></code></pre></td></tr></table></div></figure>


<p>这里可以看到，Task执行的顺序是根据定义的顺序执行的，如果还有doFirst，则先执行doFirst：</p>

<figure class='code'><figcaption><span>build.gradle </span></figcaption>
<div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="n">task</span> <span class="n">hello</span> <span class="o">&lt;&lt;</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">println</span> <span class="s1">&#39;Hello Earth&#39;</span>
</span><span class='line'><span class="o">}</span>
</span><span class='line'><span class="n">hello</span><span class="o">.</span><span class="na">doFirst</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">println</span> <span class="s1">&#39;Hello Venus&#39;</span>
</span><span class='line'><span class="o">}</span>
</span><span class='line'><span class="n">hello</span><span class="o">.</span><span class="na">doLast</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">println</span> <span class="s1">&#39;Hello Mars&#39;</span>
</span><span class='line'><span class="o">}</span>
</span><span class='line'><span class="n">hello</span> <span class="o">&lt;&lt;</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">println</span> <span class="s1">&#39;Hello Jupiter&#39;</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>执行结果如下：</p>

<figure class='code'><figcaption><span>gradle -q hello </span></figcaption>
<div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>Hello Venus
</span><span class='line'>Hello Earth
</span><span class='line'>Hello Mars
</span><span class='line'>Hello Jupiter</span></code></pre></td></tr></table></div></figure>


<h2>默认task</h2>

<p>gradle中可以定义默认的task，定义后直接使用<code>gradle</code>命令就可以执行这些task，代码如下：</p>

<figure class='code'><figcaption><span>build.gradle </span></figcaption>
<div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="n">defaultTasks</span> <span class="s1">&#39;clean&#39;</span><span class="o">,</span> <span class="s1">&#39;run&#39;</span>
</span><span class='line'><span class="n">task</span> <span class="n">clean</span> <span class="o">&lt;&lt;</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">println</span> <span class="s1">&#39;Default Cleaning!&#39;</span>
</span><span class='line'><span class="o">}</span>
</span><span class='line'><span class="n">task</span> <span class="n">run</span> <span class="o">&lt;&lt;</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">println</span> <span class="s1">&#39;Default Running!&#39;</span>
</span><span class='line'><span class="o">}</span>
</span><span class='line'><span class="n">task</span> <span class="n">other</span> <span class="o">&lt;&lt;</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">println</span> <span class="s2">&quot;I&#39;m not a default task!&quot;</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>执行结果如下：</p>

<figure class='code'><figcaption><span>gradle -q </span></figcaption>
<div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>Default Cleaning!
</span><span class='line'>Default Running!</span></code></pre></td></tr></table></div></figure>


<p>在这里，执行<code>gradle</code>等价于执行<code>gradle clean run</code>。</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[在线系统数据&服务的迁移策略]]></title>
    <link href="http://yaodanzhang.com/blog/2013/08/19/migration-strategy-for-online-system/"/>
    <updated>2013-08-19T10:11:00+08:00</updated>
    <id>http://yaodanzhang.com/blog/2013/08/19/migration-strategy-for-online-system</id>
    <content type="html"><![CDATA[<p>当需要在正在运行的在线系统中进行数据或服务的迁移时，有很多问题需要考虑，如何设计迁移策略以保证数据正确迁移，如何处理系统间的依赖，如何保证服务持续可用等等。本文将从一个服务提供者的角度，讨论如何进行数据迁移才能保证对外提供的服务接口前后一致且持续可用，实现对于客户端的无缝迁移。</p>

<p>考虑一个简单的场景，有一个web应用，存有一定数量的用户信息，以前用户的密码都是用明文存在数据库里面的，作为一个有理想有道德的程序员，我现在想升级系统，对这些密码信息进行加密，我已经准备好了加密算法和相关的实现代码，现在问题来了，我要如何加密现有用户的密码呢？</p>

<!--more -->


<p>Solution 1：写个程序在后台进行加密转换，然后重启系统，用新的代码访问加密后的数据。</p>

<p>存在的问题：如果在转换的过程中有新的数据进来或对原有数据有修改，如何处理？如何保证加密前后的数据同步？</p>

<p>Solution 2：停止服务 &mdash;> 执行加密转换 &mdash;> 重启服务，用新的代码访问加密后的数据。</p>

<p>存在的问题：也许在开发机器上测试100遍，这个过程每次都不超过1分钟，但是根据“Showcase必挂”原理，在产品环境上做同样的事情很可能就要1个小时，甚至更多。如果我的系统1分钟能挣1万块钱，你还告诉我要这么搞么？</p>

<p>那么该如何进行原有数据的加密呢？</p>

<p><img src="http://yaodanzhang.com/images/migration/Slide1.jpg" alt="Alt img" /></p>

<p>图1：未加密时的调用</p>

<p><img src="http://yaodanzhang.com/images/migration/Slide2.jpg" alt="Alt img" /></p>

<p>图2：加密后的调用</p>

<p>我们的目标是从图1转换成图2，数据的迁移肯定是不能一下子就完成的，那么我们就需要一步一步的去实现这样的<code>DAO</code>调用的转换。</p>

<h2>Step 0：现有系统接口。</h2>

<h4>Service层：</h4>

<ul>
<li><p><code>UserService</code>，用户处理接口，对外服务API</p></li>
<li><p><code>UserServiceImpl</code>，<code>UserService</code>接口的实现，调用<code>UserDAO</code></p></li>
</ul>


<h4>DAO层：</h4>

<ul>
<li><code>UserDAO</code>，用户数据访问接口</li>
</ul>


<figure class='code'><figcaption><span>UserDAO.java </span></figcaption>
<div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'><span class="kd">public</span> <span class="kd">interface</span> <span class="nc">UserDAO</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">User</span> <span class="nf">add</span><span class="o">(</span><span class="n">User</span> <span class="n">user</span><span class="o">);</span>
</span><span class='line'>
</span><span class='line'>    <span class="n">User</span> <span class="nf">update</span><span class="o">(</span><span class="n">User</span> <span class="n">user</span><span class="o">);</span>
</span><span class='line'>
</span><span class='line'>    <span class="n">User</span> <span class="nf">get</span><span class="o">(</span><span class="n">String</span> <span class="n">id</span><span class="o">);</span>
</span><span class='line'>
</span><span class='line'>    <span class="kt">boolean</span> <span class="nf">delete</span><span class="o">(</span><span class="n">String</span> <span class="n">id</span><span class="o">);</span>
</span><span class='line'>
</span><span class='line'>    <span class="kt">boolean</span> <span class="nf">existed</span><span class="o">(</span><span class="n">String</span> <span class="n">username</span><span class="o">,</span> <span class="n">String</span> <span class="n">password</span><span class="o">);</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<ul>
<li><code>UserDAOImpl</code>，<code>UserDAO</code>接口针对未加密数据的实现</li>
</ul>


<figure class='code'><figcaption><span>UserDAOImpl.java </span></figcaption>
<div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'><span class="kd">public</span> <span class="kd">class</span> <span class="nc">UserDAOImpl</span> <span class="kd">implements</span> <span class="n">UserDAO</span> <span class="o">{</span>
</span><span class='line'>    <span class="kd">private</span> <span class="n">UserMapper</span> <span class="n">userMapper</span><span class="o">;</span>
</span><span class='line'>
</span><span class='line'>    <span class="nd">@Override</span>
</span><span class='line'>    <span class="kd">public</span> <span class="n">User</span> <span class="nf">add</span><span class="o">(</span><span class="n">User</span> <span class="n">user</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="k">return</span> <span class="n">userMapper</span><span class="o">.</span><span class="na">insert</span><span class="o">(</span><span class="n">user</span><span class="o">);</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="nd">@Override</span>
</span><span class='line'>    <span class="kd">public</span> <span class="n">User</span> <span class="nf">update</span><span class="o">(</span><span class="n">User</span> <span class="n">user</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="k">return</span> <span class="n">userMapper</span><span class="o">.</span><span class="na">update</span><span class="o">(</span><span class="n">user</span><span class="o">);</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="nd">@Override</span>
</span><span class='line'>    <span class="kd">public</span> <span class="n">User</span> <span class="nf">get</span><span class="o">(</span><span class="n">String</span> <span class="n">id</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="k">return</span> <span class="n">userMapper</span><span class="o">.</span><span class="na">selectById</span><span class="o">(</span><span class="n">id</span><span class="o">);</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="nd">@Override</span>
</span><span class='line'>    <span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">delete</span><span class="o">(</span><span class="n">String</span> <span class="n">id</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="k">return</span> <span class="n">userMapper</span><span class="o">.</span><span class="na">deleteById</span><span class="o">(</span><span class="n">id</span><span class="o">);</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="nd">@Override</span>
</span><span class='line'>    <span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">existed</span><span class="o">(</span><span class="n">String</span> <span class="n">username</span><span class="o">,</span> <span class="n">String</span> <span class="n">password</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">User</span> <span class="n">user</span> <span class="o">=</span> <span class="n">userMapper</span><span class="o">.</span><span class="na">selectByUsernamePassword</span><span class="o">(</span><span class="n">username</span><span class="o">,</span> <span class="n">password</span><span class="o">);</span>
</span><span class='line'>        <span class="k">return</span> <span class="n">user</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">;</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<h2>Step 1：数据库的设计。</h2>

<p>假设以前我们的用户表有三个字段：<code>id</code>，<code>username</code>，<code>password</code>，那么我们实现的第一步是加一个字段<code>encryptedPassword</code>。</p>

<p>这样乍看起来数据库是有冗余的，但是我们如果直接修改<code>password</code>列中的内容，实质上就是删除了一个原有的列，再新加了一列，我们知道，步子迈得大了，是容易扯着蛋的，具体原因Solution &frac12; 中已经讲了。</p>

<p>相应的，我们需要增加一个<code>UserDAO</code>接口针对加密数据的实现：<code>UserDAOEncryptedImpl</code>。</p>

<figure class='code'><figcaption><span>UserDAOEncryptedImpl.java </span></figcaption>
<div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'><span class="kd">public</span> <span class="kd">class</span> <span class="nc">UserDAOEncryptedImpl</span> <span class="kd">implements</span> <span class="n">UserDAO</span> <span class="o">{</span>
</span><span class='line'>    <span class="kd">private</span> <span class="n">UserMapper</span> <span class="n">userMapperForEncryption</span><span class="o">;</span>
</span><span class='line'>
</span><span class='line'>    <span class="nd">@Override</span>
</span><span class='line'>    <span class="kd">public</span> <span class="n">User</span> <span class="nf">add</span><span class="o">(</span><span class="n">User</span> <span class="n">user</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">user</span><span class="o">.</span><span class="na">setPassword</span><span class="o">(</span><span class="n">encrypt</span><span class="o">(</span><span class="n">user</span><span class="o">.</span><span class="na">getPassword</span><span class="o">()));</span>
</span><span class='line'>        <span class="k">return</span> <span class="n">userMapperForEncryption</span><span class="o">.</span><span class="na">insert</span><span class="o">(</span><span class="n">user</span><span class="o">);</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="nd">@Override</span>
</span><span class='line'>    <span class="kd">public</span> <span class="n">User</span> <span class="nf">update</span><span class="o">(</span><span class="n">User</span> <span class="n">user</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">user</span><span class="o">.</span><span class="na">setPassword</span><span class="o">(</span><span class="n">encrypt</span><span class="o">(</span><span class="n">user</span><span class="o">.</span><span class="na">getPassword</span><span class="o">()));</span>
</span><span class='line'>        <span class="k">return</span> <span class="n">userMapperForEncryption</span><span class="o">.</span><span class="na">update</span><span class="o">(</span><span class="n">user</span><span class="o">);</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="nd">@Override</span>
</span><span class='line'>    <span class="kd">public</span> <span class="n">User</span> <span class="nf">get</span><span class="o">(</span><span class="n">String</span> <span class="n">id</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="k">return</span> <span class="n">userMapperForEncryption</span><span class="o">.</span><span class="na">selectById</span><span class="o">(</span><span class="n">id</span><span class="o">);</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="nd">@Override</span>
</span><span class='line'>    <span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">delete</span><span class="o">(</span><span class="n">String</span> <span class="n">id</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="k">return</span> <span class="n">userMapperForEncryption</span><span class="o">.</span><span class="na">deleteById</span><span class="o">(</span><span class="n">id</span><span class="o">);</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="nd">@Override</span>
</span><span class='line'>    <span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">existed</span><span class="o">(</span><span class="n">String</span> <span class="n">username</span><span class="o">,</span> <span class="n">String</span> <span class="n">password</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">User</span> <span class="n">user</span> <span class="o">=</span> <span class="n">userMapperForEncryption</span><span class="o">.</span><span class="na">selectByUsernamePassword</span><span class="o">(</span><span class="n">username</span><span class="o">,</span> <span class="n">encrypt</span><span class="o">(</span><span class="n">password</span><span class="o">));</span>
</span><span class='line'>        <span class="k">return</span> <span class="n">user</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">;</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p><code>UserDAOImpl</code>与<code>UserDAOEncryptedImpl</code>的主要区别在于这几个方法：</p>

<p>其中，对于<code>password</code>来说，<code>add()</code>，<code>update()</code>和<code>delete()</code>方法是写数据，<code>existed()</code>方法是读数据。</p>

<h2>Step 2：增加一个组合模式DAO实现。</h2>

<p><img src="http://yaodanzhang.com/images/migration/Slide3.jpg" alt="Alt img" /></p>

<p>实现代码如下：</p>

<figure class='code'><figcaption><span>CompositeUserDAOImpl.java </span></figcaption>
<div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'><span class="kd">public</span> <span class="kd">class</span> <span class="nc">CompositeUserDAOImpl</span> <span class="kd">implements</span> <span class="n">UserDAO</span> <span class="o">{</span>
</span><span class='line'>    <span class="kd">private</span> <span class="n">UserDAO</span> <span class="n">userDAOWithoutEncryption</span> <span class="o">=</span> <span class="k">new</span> <span class="n">UserDAOImpl</span><span class="o">();</span>
</span><span class='line'>
</span><span class='line'>    <span class="nd">@Override</span>
</span><span class='line'>    <span class="kd">public</span> <span class="n">User</span> <span class="nf">add</span><span class="o">(</span><span class="n">User</span> <span class="n">user</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="k">return</span> <span class="n">userDAOWithoutEncryption</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="n">user</span><span class="o">);</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="nd">@Override</span>
</span><span class='line'>    <span class="kd">public</span> <span class="n">User</span> <span class="nf">update</span><span class="o">(</span><span class="n">User</span> <span class="n">user</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="k">return</span> <span class="n">userDAOWithoutEncryption</span><span class="o">.</span><span class="na">update</span><span class="o">(</span><span class="n">user</span><span class="o">);</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="nd">@Override</span>
</span><span class='line'>    <span class="kd">public</span> <span class="n">User</span> <span class="nf">get</span><span class="o">(</span><span class="n">String</span> <span class="n">id</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="k">return</span> <span class="n">userDAOWithoutEncryption</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">id</span><span class="o">);</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="nd">@Override</span>
</span><span class='line'>    <span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">delete</span><span class="o">(</span><span class="n">String</span> <span class="n">id</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="k">return</span> <span class="n">userDAOWithoutEncryption</span><span class="o">.</span><span class="na">delete</span><span class="o">(</span><span class="n">id</span><span class="o">);</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="nd">@Override</span>
</span><span class='line'>    <span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">existed</span><span class="o">(</span><span class="n">String</span> <span class="n">username</span><span class="o">,</span> <span class="n">String</span> <span class="n">password</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="k">return</span> <span class="n">userDAOWithoutEncryption</span><span class="o">.</span><span class="na">existed</span><span class="o">(</span><span class="n">username</span><span class="o">,</span> <span class="n">password</span><span class="o">);</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>将<code>UserServiceImpl</code>中的<code>userDAO</code>改成<code>CompositeUserDAOImpl</code>的实例。</p>

<p>当前状态下，该<code>CompositeUserDAOImpl</code>对<code>user</code>的读/写都操作未加密数据，使用<code>UserDAOImpl</code>来实现。</p>

<h2>Step 3：</h2>

<p><img src="http://yaodanzhang.com/images/migration/Slide4.jpg" alt="Alt img" /></p>

<p>修改<code>CompositeUserDAOImpl</code>，使其读数据仍操作未加密数据，但写数据同时修改加密和未加密两个列。</p>

<figure class='code'><figcaption><span>CompositeUserDAOImpl.java </span></figcaption>
<div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'><span class="kd">public</span> <span class="kd">class</span> <span class="nc">CompositeUserDAOImpl</span> <span class="kd">implements</span> <span class="n">UserDAO</span> <span class="o">{</span>
</span><span class='line'>    <span class="kd">private</span> <span class="n">UserDAO</span> <span class="n">userDAOWithoutEncryption</span> <span class="o">=</span> <span class="k">new</span> <span class="n">UserDAOImpl</span><span class="o">();</span>
</span><span class='line'>    <span class="kd">private</span> <span class="n">UserDAO</span> <span class="n">userDAOWithEncryption</span> <span class="o">=</span> <span class="k">new</span> <span class="n">UserDAOEncryptedImpl</span><span class="o">();</span>
</span><span class='line'>
</span><span class='line'>    <span class="nd">@Override</span>
</span><span class='line'>    <span class="kd">public</span> <span class="n">User</span> <span class="nf">add</span><span class="o">(</span><span class="n">User</span> <span class="n">user</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">userDAOWithEncryption</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="n">user</span><span class="o">.</span><span class="na">clone</span><span class="o">());</span>
</span><span class='line'>        <span class="k">return</span> <span class="n">userDAOWithoutEncryption</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="n">user</span><span class="o">);</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="nd">@Override</span>
</span><span class='line'>    <span class="kd">public</span> <span class="n">User</span> <span class="nf">update</span><span class="o">(</span><span class="n">User</span> <span class="n">user</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">userDAOWithEncryption</span><span class="o">.</span><span class="na">update</span><span class="o">(</span><span class="n">user</span><span class="o">.</span><span class="na">clone</span><span class="o">());</span>
</span><span class='line'>        <span class="k">return</span> <span class="n">userDAOWithoutEncryption</span><span class="o">.</span><span class="na">update</span><span class="o">(</span><span class="n">user</span><span class="o">);</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="nd">@Override</span>
</span><span class='line'>    <span class="kd">public</span> <span class="n">User</span> <span class="nf">get</span><span class="o">(</span><span class="n">String</span> <span class="n">id</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="k">return</span> <span class="n">userDAOWithoutEncryption</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">id</span><span class="o">);</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="nd">@Override</span>
</span><span class='line'>    <span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">delete</span><span class="o">(</span><span class="n">String</span> <span class="n">id</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">userDAOWithEncryption</span><span class="o">.</span><span class="na">delete</span><span class="o">(</span><span class="n">id</span><span class="o">);</span>
</span><span class='line'>        <span class="k">return</span> <span class="n">userDAOWithoutEncryption</span><span class="o">.</span><span class="na">delete</span><span class="o">(</span><span class="n">id</span><span class="o">);</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="nd">@Override</span>
</span><span class='line'>    <span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">existed</span><span class="o">(</span><span class="n">String</span> <span class="n">username</span><span class="o">,</span> <span class="n">String</span> <span class="n">password</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="k">return</span> <span class="n">userDAOWithoutEncryption</span><span class="o">.</span><span class="na">existed</span><span class="o">(</span><span class="n">username</span><span class="o">,</span> <span class="n">password</span><span class="o">);</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<h2>Step 4：开始进行数据迁移。</h2>

<p>可以写个脚本在后台执行，将<code>password</code>列中的数据加密后存入<code>encryptedPassword</code>列。</p>

<h2>Step 5：</h2>

<p><img src="http://yaodanzhang.com/images/migration/Slide5.jpg" alt="Alt img" /></p>

<p>数据迁移完成后，修改<code>CompositeUserDAOImpl</code>，使其读数据从加密数据列中读取，写数据仍同时修改加密和未加密两个列。</p>

<figure class='code'><figcaption><span>CompositeUserDAOImpl.java </span></figcaption>
<div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'><span class="kd">public</span> <span class="kd">class</span> <span class="nc">CompositeUserDAOImpl</span> <span class="kd">implements</span> <span class="n">UserDAO</span> <span class="o">{</span>
</span><span class='line'>    <span class="kd">private</span> <span class="n">UserDAO</span> <span class="n">userDAOWithoutEncryption</span> <span class="o">=</span> <span class="k">new</span> <span class="n">UserDAOImpl</span><span class="o">();</span>
</span><span class='line'>    <span class="kd">private</span> <span class="n">UserDAO</span> <span class="n">userDAOWithEncryption</span> <span class="o">=</span> <span class="k">new</span> <span class="n">UserDAOEncryptedImpl</span><span class="o">();</span>
</span><span class='line'>
</span><span class='line'>    <span class="nd">@Override</span>
</span><span class='line'>    <span class="kd">public</span> <span class="n">User</span> <span class="nf">add</span><span class="o">(</span><span class="n">User</span> <span class="n">user</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">userDAOWithoutEncryption</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="n">user</span><span class="o">);</span>
</span><span class='line'>        <span class="k">return</span> <span class="n">userDAOWithEncryption</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="n">user</span><span class="o">.</span><span class="na">clone</span><span class="o">());</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="nd">@Override</span>
</span><span class='line'>    <span class="kd">public</span> <span class="n">User</span> <span class="nf">update</span><span class="o">(</span><span class="n">User</span> <span class="n">user</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">userDAOWithoutEncryption</span><span class="o">.</span><span class="na">update</span><span class="o">(</span><span class="n">user</span><span class="o">.</span><span class="na">clone</span><span class="o">());</span>
</span><span class='line'>        <span class="k">return</span> <span class="n">userDAOWithEncryption</span><span class="o">.</span><span class="na">update</span><span class="o">(</span><span class="n">user</span><span class="o">);</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="nd">@Override</span>
</span><span class='line'>    <span class="kd">public</span> <span class="n">User</span> <span class="nf">get</span><span class="o">(</span><span class="n">String</span> <span class="n">id</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="k">return</span> <span class="n">userDAOWithEncryption</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">id</span><span class="o">);</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="nd">@Override</span>
</span><span class='line'>    <span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">delete</span><span class="o">(</span><span class="n">String</span> <span class="n">id</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">userDAOWithoutEncryption</span><span class="o">.</span><span class="na">delete</span><span class="o">(</span><span class="n">id</span><span class="o">);</span>
</span><span class='line'>        <span class="k">return</span> <span class="n">userDAOWithEncryption</span><span class="o">.</span><span class="na">delete</span><span class="o">(</span><span class="n">id</span><span class="o">);</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="nd">@Override</span>
</span><span class='line'>    <span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">existed</span><span class="o">(</span><span class="n">String</span> <span class="n">username</span><span class="o">,</span> <span class="n">String</span> <span class="n">password</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="k">return</span> <span class="n">userDAOWithEncryption</span><span class="o">.</span><span class="na">existed</span><span class="o">(</span><span class="n">username</span><span class="o">,</span> <span class="n">password</span><span class="o">);</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<h2>Step 6：</h2>

<p><img src="http://yaodanzhang.com/images/migration/Slide6.jpg" alt="Alt img" /></p>

<p>上线运行一段时间后，当确保<code>password</code>列中的数据已经完全正确迁移并且没有其他的程序依赖与它，便可以将这一列移除了。<code>CompositeUserDAOImpl</code>中将只调用加密数据的<code>DAO</code>。</p>

<figure class='code'><figcaption><span>CompositeUserDAOImpl.java </span></figcaption>
<div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'><span class="kd">public</span> <span class="kd">class</span> <span class="nc">CompositeUserDAOImpl</span> <span class="kd">implements</span> <span class="n">UserDAO</span> <span class="o">{</span>
</span><span class='line'>    <span class="kd">private</span> <span class="n">UserDAO</span> <span class="n">userDAOWithEncryption</span> <span class="o">=</span> <span class="k">new</span> <span class="n">UserDAOEncryptedImpl</span><span class="o">();</span>
</span><span class='line'>
</span><span class='line'>    <span class="nd">@Override</span>
</span><span class='line'>    <span class="kd">public</span> <span class="n">User</span> <span class="nf">add</span><span class="o">(</span><span class="n">User</span> <span class="n">user</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="k">return</span> <span class="n">userDAOWithEncryption</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="n">user</span><span class="o">.</span><span class="na">clone</span><span class="o">());</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="nd">@Override</span>
</span><span class='line'>    <span class="kd">public</span> <span class="n">User</span> <span class="nf">update</span><span class="o">(</span><span class="n">User</span> <span class="n">user</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="k">return</span> <span class="n">userDAOWithEncryption</span><span class="o">.</span><span class="na">update</span><span class="o">(</span><span class="n">user</span><span class="o">);</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="nd">@Override</span>
</span><span class='line'>    <span class="kd">public</span> <span class="n">User</span> <span class="nf">get</span><span class="o">(</span><span class="n">String</span> <span class="n">id</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="k">return</span> <span class="n">userDAOWithEncryption</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">id</span><span class="o">);</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="nd">@Override</span>
</span><span class='line'>    <span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">delete</span><span class="o">(</span><span class="n">String</span> <span class="n">id</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="k">return</span> <span class="n">userDAOWithEncryption</span><span class="o">.</span><span class="na">delete</span><span class="o">(</span><span class="n">id</span><span class="o">);</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="nd">@Override</span>
</span><span class='line'>    <span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">existed</span><span class="o">(</span><span class="n">String</span> <span class="n">username</span><span class="o">,</span> <span class="n">String</span> <span class="n">password</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="k">return</span> <span class="n">userDAOWithEncryption</span><span class="o">.</span><span class="na">existed</span><span class="o">(</span><span class="n">username</span><span class="o">,</span> <span class="n">password</span><span class="o">);</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[六个人的毕业旅行]]></title>
    <link href="http://yaodanzhang.com/blog/2013/05/04/graduation-trip-of-6-huster/"/>
    <updated>2013-05-04T12:44:00+08:00</updated>
    <id>http://yaodanzhang.com/blog/2013/05/04/graduation-trip-of-6-huster</id>
    <content type="html"><![CDATA[<p>旅行结束了快两个月了，一直想写点什么，却一直下不了笔，大概这就是传说中的拖延病把。这次在公司里做Pecha Kucha，我想讲讲自己的这次旅行，也正好借这个机会，给自己的毕业旅行做个总结。</p>

<p>－－－－－－－－－－－－－－－－－       我是分割线     －－－－－－－－－－－－－－－－－－</p>

<p>这是一个我期待了近两年的旅行。</p>

<p>去年10月份找到工作后，我和<a href="http://weibo.com/meikidd">@meikidd</a>就开始了筹划。无奈大家总有各种事情，凑不齐时间，这样一拖，就到过年了。过完年，要是再定不下来，就再没时间去了，以前一届又一届的毕业旅行就是这样胎死腹中的。于是我和<a href="http://weibo.com/meikidd">@meikidd</a>决定不等了，他带上<a href="http://weibo.com/kimismj">@up-up-努力向上的S</a>，就我和他们小两口三个人去。定好行程和机票酒店后，又拉来了去年毕业的师姐<a href="http://weibo.com/ireneshy">@irene</a>和他老公<a href="http://weibo.com/u/2948037661">@kim1219</a>，临出发前又拉上了学妹<a href="http://weibo.com/u/1974590105">@只是很无趣</a>，6个人的小团体就这样成型了。</p>

<!--more -->


<p>去云南几乎是大家一瞬间就达成的共识，在与筒子们制定计划的过程中，有过鼓浪屿，有过西沙群岛，但由于各种原因都被否决掉了，在这个过程中，唯一没有人提过反对意见的就是云南，可以看到，彩云之南的魅力已经深入了我们大家的血液。</p>

<p><img src="http://yaodanzhang.com/images/800/0-1.jpg" alt="Alt img" /></p>

<p>2013年3月7日上午10点，我们六个人在昆明长水机场汇合，美丽的旅行正式开始。</p>

<h1>Day1： 昆明</h1>

<p>昆明本不在我们的计划之中，却是我们去云南旅行的必经之路。早上到昆明的飞机，晚上11点从昆明出发的火车，这给了我们一天的时间，于是石林、滇池这些离市区较远的景点被我们pass掉了，最终决定在翠湖附近的几个地方随便逛逛。</p>

<p>第一站是云南省博物馆。我们一行人对这里并不失望，倒不是因为这里多好，而是我们本来就没抱有希望。云南自古属蛮夷之地，游离在中原文明之外，通常以展示古人类文明遗迹为主的博物馆自然比不上中原地区，可能也正因为被人类的活动影响很小，才让云南留下了这么多美丽的地方吧，上帝总是公平的。（在这里顺便给湖北省博物馆做个植入广告，驴友们要来武汉玩，可以不去东湖、不去户部巷、甚至可以不去黄鹤楼，但是一定要挤出时间去湖北省博物馆，相信你们不会失望的。跑题了跑题了&hellip;&hellip;）</p>

<p><img src="http://yaodanzhang.com/images/800/1-01.jpg" alt="Alt img" /></p>

<p>古人的生殖崇拜</p>

<p><img src="http://yaodanzhang.com/images/800/1-02.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/1-5.jpg" alt="Alt img" /></p>

<p>3楼的民俗展还挺不错的</p>

<p>逛博物馆没花多长时间，出来就到饭点了。到昆明之前，看了好多攻略，都说路边小店的小锅米线要比闻名的过桥米线好吃，于是我们找了一个人气很旺的小锅米线店子，说实话，这次很失望，同我们后来在丽江吃的过桥米线相比，差距还是很大的。当然了，这也许只是个人口味不同，大家做个参考就好。</p>

<p><img src="http://yaodanzhang.com/images/800/1-08.jpg" alt="Alt img" /></p>

<p>臭豆腐米线，不好吃的说</p>

<p>我们一行人吃完午饭接着就去了翠湖公园，远远就看到了传说中的红嘴鸥，几千只红嘴鸥同时起飞的时候，相当壮观。翠湖的红嘴鸥估计和人们待久了，长期被人喂食，一点也不怕人，不光不怕人，吃东西的技术也是相当的好，湖边人们扔到天上的饼干，总能被它们直接接住，很少有落到湖里的，赞一个。</p>

<p><img src="http://yaodanzhang.com/images/800/1-03.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/1-04.jpg" alt="Alt img" /></p>

<p>翠湖的红嘴鸥</p>

<p><img src="http://yaodanzhang.com/images/800/1-9.jpg" alt="Alt img" /></p>

<p>漫天的红嘴鸥，甚是壮观</p>

<p><img src="http://yaodanzhang.com/images/800/1-15.jpg" alt="Alt img" /></p>

<p>我们的最后一站是陆军讲武堂。我是怀着对蔡锷将军深深的敬意去的，找了半个小时，终于在一片最小的照片中看到了他的身影，嗯，不错，真“低调”，好，好！</p>

<p><img src="http://yaodanzhang.com/images/800/1-20.jpg" alt="Alt img" /></p>

<p>讲武堂的操场（有木有监狱的赶脚？）</p>

<p><img src="http://yaodanzhang.com/images/800/1-24.jpg" alt="Alt img" /></p>

<p>我们在昆明的晚餐十分精彩。在点评上找的餐厅，“傣家竹楼”，在云南省博物馆附近。那里的服务员都是傣族妹子，特别可爱，我们吃了两个小时，就听她们在一旁嬉笑打闹了两个小时，超级欢乐的。菠萝紫米饭、柠檬馓子、泼水粑粑、包烧豆腐。。。。。。味道都很特别，都很不错，更多的就不说了，直接上图吧：</p>

<p><img src="http://yaodanzhang.com/images/800/1-21.jpg" alt="Alt img" /></p>

<p>柠檬馓子，也就是柠檬味儿的粉丝，很酸、很开胃、很好吃</p>

<p><img src="http://yaodanzhang.com/images/800/1-22.jpg" alt="Alt img" /></p>

<p>菠萝紫米饭，糯米+黑米，类似四川的酒米饭</p>

<p><img src="http://yaodanzhang.com/images/800/1-23.jpg" alt="Alt img" /></p>

<p>包烧豆腐，我一直对吃豆腐这件事情有独钟，赞！</p>

<p>我们几个走了一整天，都累坏了，吃完饭就直接去了火车站，没再出去玩。我们买的昆明到大理的软卧，票价不贵，就100多一点。我也有史以来第一次参观了软卧的候车区，嗯，很不错。大家无聊了就开始玩游戏，我们研究了一个新游戏，每个人写一个名词给别人，每个人可以看到别人手上的词，不能看自己的，然后依次提问题，每次提的问题只能用是和否来回答，最后猜自己手上的是什么。看起来是很无聊的一个游戏，玩起来却相当好玩，好玩的地方主要是过程中大家的机智幽默问题和解答，还有瞬间迸发的灵感，说不太清楚，大家玩过就知道了。</p>

<h1>Day2 &ndash; Day3：大理（古镇、洱海、双廊）</h1>

<p><img src="http://yaodanzhang.com/images/800/2-59.jpg" alt="Alt img" /></p>

<p>在火车上睡了一宿，第二天早上6点多就到了大理。网上诸多攻略都说从火车站去大理古城，最好的方式是做公交车，可以好好欣赏沿途的风景。这话不假，但有个前提，是能找到座位，我就在车上被挤着站了一路，实在是找不到好的角度去欣赏。不想去挤公交的同学可以考虑拼车去古城，价格也不是很贵，网上有相关攻略，大家可以查查。</p>

<p>公交车（忘了是哪一路了，8路？）终点站在古城的苍山门，就在苍山脚下，到的时候刚日出不久，苍山笼罩在一层薄薄的雾气中，映着朝阳暖暖的红光，那是相当的美。</p>

<p><img src="http://yaodanzhang.com/images/800/2-1.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/2-2.jpg" alt="Alt img" /></p>

<p>古镇北门，也就是苍山门</p>

<p>不得不感慨的是，大理的人们生活太悠闲了，早上9点多了，古城里的店子基本都没开门，我们好不容易找了家小店，随便吃了点，当是早餐了。我们在大理最主要的计划是单车环洱海，于是吃完早饭我们就去找租车行了。在复兴路上找了一家，“龙云租车行”，老板还不错，价格不算贵。我们6个人租了5个车子，一辆捷安特的双人车（<a href="http://weibo.com/kimismj">@up-up-努力向上的S</a>不会骑车，<a href="http://weibo.com/meikidd">@meikidd</a>童鞋得带着她）、3辆捷安特Hunter 2.0（这个车其实不好骑，我们主要是不想背包，想绑在后架上，这车唯一的优点就是那后架，杠杠的，用过都说好）、1辆没后架的捷安特（据<a href="http://weibo.com/u/2948037661">@kim1219</a>童鞋透露，很好骑）。</p>

<p><img src="http://yaodanzhang.com/images/800/2-3.jpg" alt="Alt img" /></p>

<p>租车行就在复兴路上面，复兴路的租车店很多，我们选这家主要是因为只有这家开了门（Orz&hellip;&hellip;）</p>

<p><img src="http://yaodanzhang.com/images/800/2-5.jpg" alt="Alt img" /></p>

<p>五车编队</p>

<p>10点多，我们出发了。</p>

<p>这次骑行的路线是顺时针环湖：大理古镇 － 喜洲 － 上关 － 双廊 － 下关 － 古镇，分两天骑完，中间在双廊住一晚。这条线环湖是驴友们最常选的方向，不过现在也有越来越多的人选择逆时针环湖，有兴趣的朋友也可以试试。</p>

<p>确定第一天的线路颇费了些周折，从古镇经上关到双廊的骑行线路驴友们一般选择走221省道（大丽线），但是大丽线一路都离洱海很远，沿途景色是远远不如环海西路（双廊到下关）的，在临出发前我突然在马蜂窝上看到一个驴友说还有一条环海东路，车少路况好，而且最重要的是紧邻洱海，但是这条路没有一个地图上有标出来，到了大理能否找到，在到大理之前我们心里也没底。但事实证明我们的担心是多余的，大家稍微打听一下，当地居民基本上都知道的，非常好找。</p>

<p><img src="http://yaodanzhang.com/images/800/2-9.jpg" alt="Alt img" /></p>

<p>环海东路</p>

<p><img src="http://yaodanzhang.com/images/800/2-8.jpg" alt="Alt img" /></p>

<p>洱海东边有很多小溪流入洱海，3月份的时候春天的气息已经很浓了</p>

<p><img src="http://yaodanzhang.com/images/800/2-6.jpg" alt="Alt img" /></p>

<p>前方100米还有个景点，叫C罗</p>

<p><img src="http://yaodanzhang.com/images/800/2-10.jpg" alt="Alt img" /></p>

<p>我的座骑，篮子和后架很皮实</p>

<p><img src="http://yaodanzhang.com/images/800/2-12.jpg" alt="Alt img" /></p>

<p>我们的午餐地点，一个白族小院儿，建筑的白族民居风格已不明显，但小院儿的很多角落还是很有特色的</p>

<p><img src="http://yaodanzhang.com/images/800/2-13.jpg" alt="Alt img" /></p>

<p>风吹麦浪</p>

<p><img src="http://yaodanzhang.com/images/800/2-14.jpg" alt="Alt img" /></p>

<p>3月的洱海正好油菜花开，走221省道的话沿途的油菜地会更壮观一些</p>

<p><img src="http://yaodanzhang.com/images/800/2-15.jpg" alt="Alt img" /></p>

<p><a href="http://weibo.com/meikidd">@meikidd</a>童鞋的双动力自行车，走到上关就坏了，掉了一个关键的螺丝。幸好在镇子上坏的，不然他俩可就惨了。</p>

<p>我们在前半段不停的拍照，浪费了不少时间，快下午1点了还没到喜洲。吃饭休息用了快一个小时，我们逐渐意识到得加快脚步了，不然可能天黑也到不了双廊。其实从上关到双廊沿途的风景远远比从古镇到上关的漂亮，无奈我们急着赶路，停下来拍照就少多了，可惜了可惜了。</p>

<p><img src="http://yaodanzhang.com/images/800/2-22.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/2-19.jpg" alt="Alt img" /></p>

<p>环海西路（上关到双廊）的日落</p>

<p>晚上6点左右，终于到了双廊，可惜的是太阳已经下山，没有能够在传说中的海地看到传说中的日落。</p>

<p><img src="http://yaodanzhang.com/images/800/2-18.jpg" alt="Alt img" /></p>

<p>海地的大名想必去过云南的驴友们都是听说过的，十分幸运，我们提前近一个月订到了海地的床位。</p>

<p><img src="http://yaodanzhang.com/images/800/2-20.jpg" alt="Alt img" /></p>

<p>这是海地神秘的四号院，1680一晚，闲人免进</p>

<p><img src="http://yaodanzhang.com/images/800/2-21.jpg" alt="Alt img" /></p>

<p>偷拍美女</p>

<p><img src="http://yaodanzhang.com/images/800/2-40.jpg" alt="Alt img" /></p>

<p>露台，去海地必拍的地方</p>

<p><img src="http://yaodanzhang.com/images/800/2-24.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/2-26.jpg" alt="Alt img" /></p>

<p>海景8人间，虽然是8人间，但是住着非常舒服</p>

<p><img src="http://yaodanzhang.com/images/800/2-25.jpg" alt="Alt img" /></p>

<p>海地很有特色的灯</p>

<p><img src="http://yaodanzhang.com/images/800/2-36.jpg" alt="Alt img" /></p>

<p>小院儿</p>

<p><img src="http://yaodanzhang.com/images/800/2-32.jpg" alt="Alt img" /></p>

<p>lobby</p>

<p><img src="http://yaodanzhang.com/images/800/2-35.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/2-30.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/2-31.jpg" alt="Alt img" /></p>

<p>lobby里面很多这样的留言册</p>

<p><img src="http://yaodanzhang.com/images/800/2-32-1.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/2-39.jpg" alt="Alt img" /></p>

<p><a href="http://weibo.com/meikidd">@meikidd</a>的留言，很有感</p>

<p><img src="http://yaodanzhang.com/images/800/2-37.jpg" alt="Alt img" /></p>

<p><a href="http://weibo.com/kimismj">@up-up-努力向上的S</a>在努力作画，据说画的是小桥流水人家，有人能看出来吗？</p>

<p><img src="http://yaodanzhang.com/images/800/2-33.jpg" alt="Alt img" /></p>

<p>小院儿里很多这样的Tips</p>

<p><img src="http://yaodanzhang.com/images/800/2-38.jpg" alt="Alt img" /></p>

<p>忘了它叫啥名字了，很可爱，很温顺，任由我们欺负。那天晚上就睡在我们门外，有个女孩儿要起来上厕所，它愣是拦在门口不让她出去</p>

<p><img src="http://yaodanzhang.com/images/800/2-27.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/2-28.jpg" alt="Alt img" /></p>

<p>海地的清晨，“面朝大海，春暖花开”是蹦到我脑子里的第一句话。</p>

<p><img src="http://yaodanzhang.com/images/800/2-41.jpg" alt="Alt img" /></p>

<p>第一天的骑行结束后，把我们几个人累坏了。除了<a href="http://weibo.com/meikidd">@meikidd</a>，我们几个人都是第一次骑行这么长的距离，最难受的是屁股，一坐下来就疼。第二天起床的时候，我们几个人已经决定不骑了，就在出发前的半个小时，我去上了个大，然后回来就听组织决定，继续骑行，我到现在也还没搞明白，我上大的那10分钟发生了些神马。。。。。。</p>

<p>虽然决定继续，但是上路半个小时后，<a href="http://weibo.com/ireneshy">@irene</a>还是坚持不住，和<a href="http://weibo.com/u/2948037661">@kim1219</a>坐车回去了，这样就只剩我们4人。环海西路同环海东路差别巨大，环海东路基本上没有上下坡，但环海西路从双廊开始，基本是连续的坡道。骑上坡很痛苦，但是上完后下坡的过程很high，刚开始我们几个都没摁刹车的，直接放下去，但我后来感觉捷安特的Hunter 2.0有点扛不住，几个下坡过后我就不敢再这么放了。到底是个菜车，还是不敢使劲儿折腾</p>

<p><img src="http://yaodanzhang.com/images/800/2-42.jpg" alt="Alt img" /></p>

<p>这是我们遇到的第一个长上坡，这时大家的兴致还是很高的</p>

<p><img src="http://yaodanzhang.com/images/800/2-46.jpg" alt="Alt img" /></p>

<p>骑到这儿，我们就绝望了，这里是环海西路上唯一一段还没修好路，又烂又窄又有很多大货车</p>

<p><img src="http://yaodanzhang.com/images/800/2-47.jpg" alt="Alt img" /></p>

<p>我们最后决定搭车上山，这哥们儿被我们拦了下来，他的小三轮儿只能勉强装下我们的3辆车，而且固定不了，于是他在前面开车，我和<a href="http://weibo.com/meikidd">@meikidd</a>在后面扶着自行车跟着跑，大货车一过，我俩满嘴满鼻子都是沙。</p>

<p>最后再赞一个，谢谢这位大哥了，当地的居民很淳朴，大家路上有什么困难他们都很乐意帮忙的。</p>

<p><img src="http://yaodanzhang.com/images/800/2-63.jpg" alt="Alt img" /></p>

<p>在这样的长下坡飙车很high</p>

<p><img src="http://yaodanzhang.com/images/800/2-45.jpg" alt="Alt img" /></p>

<p>这是航母style么？</p>

<p><img src="http://yaodanzhang.com/images/800/2-62.jpg" alt="Alt img" /></p>

<p>下关，那几天云南发生了好几场森林火灾，这是直升机来洱海取水灭火，想不通的是这么一点水能起啥作用？</p>

<p>从下关开始，就没有环湖的路线了，我们只能沿着221省道骑完了最后的10公里。</p>

<p><img src="http://yaodanzhang.com/images/800/2-61.jpg" alt="Alt img" /></p>

<p>从古镇山脚下到古镇的最后一段上坡路，<a href="http://weibo.com/meikidd">@meikidd</a>、<a href="http://weibo.com/kimismj">@up-up-努力向上的S</a>、<a href="http://weibo.com/u/1974590105">@只是很无趣</a>三人愣是骑了上去。<a href="http://weibo.com/meikidd">@meikidd</a>和<a href="http://weibo.com/kimismj">@up-up-努力向上的S</a>是双核双动力，也就罢了，<a href="http://weibo.com/u/1974590105">@只是很无趣</a>也骑了上去，看来这真是小个子有大能量啊。</p>

<p><img src="http://yaodanzhang.com/images/800/2-49.jpg" alt="Alt img" /></p>

<p>我们在古镇的居所：春夏秋冬青旅</p>

<p><img src="http://yaodanzhang.com/images/800/2-51.jpg" alt="Alt img" /></p>

<p>古镇角落1：谁能告诉我双人间、标间、夫妻间有啥区别？</p>

<p><img src="http://yaodanzhang.com/images/800/2-50.jpg" alt="Alt img" /></p>

<p>古镇角落2：<a href="http://weibo.com/u/2948037661">@kim1219</a> &amp; 酒吧</p>

<p><img src="http://yaodanzhang.com/images/800/2-52.jpg" alt="Alt img" /></p>

<p>古镇角落3</p>

<p><img src="http://yaodanzhang.com/images/800/2-53.jpg" alt="Alt img" /></p>

<p>古镇角落4</p>

<p><img src="http://yaodanzhang.com/images/800/2-54.jpg" alt="Alt img" /></p>

<p>古镇角落5：霸气的学校</p>

<p><img src="http://yaodanzhang.com/images/800/2-55.jpg" alt="Alt img" /></p>

<p>古镇角落6：Bad Monkey酒吧</p>

<p><img src="http://yaodanzhang.com/images/800/2-56.jpg" alt="Alt img" /></p>

<p>古镇角落7：街头艺人，这个老外旁边围了一圈女fans</p>

<p><img src="http://yaodanzhang.com/images/800/2-57.jpg" alt="Alt img" /></p>

<p>晚餐 &ndash; 玫瑰炒鸡蛋</p>

<p><img src="http://yaodanzhang.com/images/800/2-58.jpg" alt="Alt img" /></p>

<p>晚餐 &ndash; 海菜：洱海中的水草，味道很不错</p>

<p><img src="http://yaodanzhang.com/images/800/2-60.jpg" alt="Alt img" /></p>

<p>离开大理的清晨</p>

<p>大理有风花雪月四景，在大理的两天，我们没看到上关的花、苍山的雪和洱海的月，倒是结结实实的感受了一把下关的风。其实这风不光光是在下关有，我感觉一路骑行一路都在迎着风走，我一路走一路在心里默念：“零级烟柱直冲天，一级青烟随风偏，二级清风吹脸面……”。我在古镇住的那一晚，洗完衣服拿到楼顶去晾，我先把头巾挂上，等我晾完别的衣服回来，头巾已经被吹干了，真事儿，没夸张。ps.出发之前，租车行的老板一再嘱咐我们一定小心，别被风吹到湖里了，到了下关后，我算是相信他这话不是没根据的了，呵呵。</p>

<p>在大理的两天，赶上了附近几场大火，烟雾漂到洱海上，隔着湖也看不到对岸，没看到美景这算是第一个遗憾；没有时间在海地多待几天，这算是第二个遗憾。虽有些遗憾，但是一路的骑行、一路的快乐，在我心中深深刻下了一个美丽的大理！</p>

<h1>Day4：丽江古镇</h1>

<p>我一直不喜欢在城市旅行，只是作为中转，不得不去，昆明如是，丽江也如是。</p>

<p><img src="http://yaodanzhang.com/images/800/3-5.jpg" alt="Alt img" /></p>

<p>我们中午到的丽江，到了之后的第一件事就是找客栈。丽江客栈多，加上又是淡季，我们就没提前订。本来准备随便找家有特色的住下，但看了好几家，都没有什么感觉，最后还是找了一家挺有名的青旅：老谢车马店。</p>

<p><img src="http://yaodanzhang.com/images/800/3-1.jpg" alt="Alt img" /></p>

<p>古镇角落（丽江版）1</p>

<p><img src="http://yaodanzhang.com/images/800/3-2.jpg" alt="Alt img" /></p>

<p>古镇角落2</p>

<p><img src="http://yaodanzhang.com/images/800/3-3.jpg" alt="Alt img" /></p>

<p>古镇角落3：某个带有黑社会性质反清组织的堂口</p>

<p><img src="http://yaodanzhang.com/images/800/3-4.jpg" alt="Alt img" /></p>

<p>古镇角落4</p>

<p><img src="http://yaodanzhang.com/images/800/3-6.jpg" alt="Alt img" /></p>

<p>古镇角落5：唱歌的姑娘很妩媚</p>

<p><img src="http://yaodanzhang.com/images/800/3-22.jpg" alt="Alt img" /></p>

<p>古镇角落6：囧</p>

<p>白天丽江的人那个多啊，现在还是淡季，都人挤人，我都不敢想旺季该是什么样子。我们在古城逛了一圈，没什么意思，就回客栈休息了。发呆、睡觉、听歌、逗狗、拍照、发微博、玩积木、桌上足球，就这样过了一下午。</p>

<p><img src="http://yaodanzhang.com/images/800/3-7.jpg" alt="Alt img" /></p>

<p>老谢一隅1：这个小狗很酷的，根本不屌我们</p>

<p><img src="http://yaodanzhang.com/images/800/3-20.jpg" alt="Alt img" /></p>

<p>老谢一隅2</p>

<p><img src="http://yaodanzhang.com/images/800/3-21.jpg" alt="Alt img" /></p>

<p>老谢一隅3</p>

<p><img src="http://yaodanzhang.com/images/800/3-8.jpg" alt="Alt img" /></p>

<p>老谢一隅4</p>

<p><img src="http://yaodanzhang.com/images/800/3-11.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/3-12.jpg" alt="Alt img" /></p>

<p>傍晚的时候，我们出去吃饭，看到夜色中的丽江古城，没有多少行人的街道很安静，路边小店的灯光很温馨、也很暧昧，这才是这个艳遇之都原本的颜色，我也才发现，丽江这座城市，最美的也就是这个时候了吧。</p>

<p><img src="http://yaodanzhang.com/images/800/3-19.jpg" alt="Alt img" /></p>

<p>纳西妹子</p>

<p><img src="http://yaodanzhang.com/images/800/3-10.jpg" alt="Alt img" /></p>

<p>坝坝舞，<a href="http://weibo.com/meikidd">@meikidd</a>和<a href="http://weibo.com/kimismj">@up-up-努力向上的S</a>进去跳了一段，跟不上节奏啊。</p>

<p>在寻找晚餐的过程中，我们进的第一家店用的pad点菜，霸气侧漏啊有木有，我们看了下菜单，再看了下价格就很自觉的出来了。后来又走了好久，走到古城外了都没找到合适的饭店，吃了点米线将就了，油多量少还难吃。回老谢的路上，看到一家过桥米线的店，又进去吃了一顿，很不错。</p>

<p><img src="http://yaodanzhang.com/images/800/3-13.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/3-16.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/3-17.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/3-18.jpg" alt="Alt img" /></p>

<p>用脸盆儿装的米线，实在有些夸张</p>

<p>丽江的行程很匆忙，没有仔细去逛，也没准备仔细去逛。留下印象的东西不多，除了美丽的夜景，也就剩下丽江小倩的歌声了。刚到丽江，就发现满街都是音像店，都放着小倩的cd，老板都跟着节奏打着非洲手鼓，这也算是这里的一大特色吧。</p>

<h1>Day5 &ndash; Day8：泸沽湖（里格、女神湾）</h1>

<p>泸沽湖是这次旅行的最后一站，也是最重要、最令我们期待的一站，一共9天的时间，我们就为泸沽湖安排了4天。</p>

<p>去泸沽湖原本的计划是坐大巴过去，在老谢车马店准备订票的时候才临时决定的包车。从丽江到泸沽湖的大巴来回150，加上门票100，一个人要250块钱的路费，我们6个人包一辆车去4天一共1800，一个人也贵不了多少钱，而且去的时候线路和大巴车不一样，稍微远一些，但是风景好太多了。</p>

<p>这条路在玉龙雪山背后，我们先从海拔2000米的丽江到海拔4000米，接着下到金沙江（海拔估计2000米左右），再一路向上，到海拔3000米的泸沽湖，司机木师傅的老家就在这条路上的一个村子里，所以知道这条路。</p>

<p>从丽江古城出来，先是一段非常颠簸的土路，这条路上有好几个采石场。过了这几个采石场，路况就变得特别好，听木师傅说这条路已经修好了好几年了，但是路况跟刚修好的时候一样好，主要是因为这条路上车少，我们一路到泸沽湖走了9个小时，路上遇到的车加上脚趾头是能数得过来的。</p>

<p><img src="http://yaodanzhang.com/images/800/4-2.jpg" alt="Alt img" /></p>

<p>这是过了采石场不远，第一个可以看到玉龙雪山的地方，公路旁的空地上满满的是牛粪，风干后的牛粪，Orz…</p>

<p><img src="http://yaodanzhang.com/images/800/4-1.jpg" alt="Alt img" /></p>

<p>车匪路霸</p>

<p><img src="http://yaodanzhang.com/images/800/4-3.jpg" alt="Alt img" /></p>

<p>沿途风景那个美啊，我坐副驾驶上面，完全控制不住我自己，使劲儿的摁快门儿，很快就把相机的内存用光了。</p>

<p><img src="http://yaodanzhang.com/images/800/4-4.jpg" alt="Alt img" /></p>

<p>这里前两天刚遭了一场山火</p>

<p><img src="http://yaodanzhang.com/images/800/4-5.jpg" alt="Alt img" /></p>

<p>海拔4000米，我们路上途经的最高点</p>

<p><img src="http://yaodanzhang.com/images/800/4-7.jpg" alt="Alt img" /></p>

<p>海拔4000米的冰瀑布</p>

<p><img src="http://yaodanzhang.com/images/800/4-8.jpg" alt="Alt img" /></p>

<p>远远的雪山</p>

<p><img src="http://yaodanzhang.com/images/800/4-9.jpg" alt="Alt img" /></p>

<p>出了丽江，山火的烟雾渐渐散去，天渐渐变蓝，上次见到这么蓝的天已经不知道是什么时候了。</p>

<p><img src="http://yaodanzhang.com/images/800/4-10.jpg" alt="Alt img" /></p>

<p>下山途中，最下面绿色的河水就是金沙江，就这点距离，我们走了好几个小时。</p>

<p><img src="http://yaodanzhang.com/images/800/4-11.jpg" alt="Alt img" /></p>

<p>下山途中的村子，也是司机木师傅的家乡，我们在这里吃的午饭</p>

<p><img src="http://yaodanzhang.com/images/800/4-12.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/4-13.jpg" alt="Alt img" /></p>

<p>大山里的孩子</p>

<p><img src="http://yaodanzhang.com/images/800/4-14-1.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/4-14.jpg" alt="Alt img" /></p>

<p>大山深处的农田</p>

<p><img src="http://yaodanzhang.com/images/800/4-17.jpg" alt="Alt img" /></p>

<p>乘渡轮过江</p>

<p><img src="http://yaodanzhang.com/images/800/4-15.jpg" alt="Alt img" /></p>

<p>福尔摩斯时间：条件一，这渡轮是木师傅村子里最有钱的一家人买的，花了300多万，半年就收回了成本。条件二，照片中的桥修了好几年了都没修好。问：为什么桥总修不好呢？</p>

<p><img src="http://yaodanzhang.com/images/800/4-18.jpg" alt="Alt img" /></p>

<p>我们老把这儿叫成鸭绿江，改不过来了。</p>

<p><img src="http://yaodanzhang.com/images/800/4-19.jpg" alt="Alt img" /></p>

<p>从“鸭绿江”上来，又进了大山</p>

<p><img src="http://yaodanzhang.com/images/800/4-20.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/4-21.jpg" alt="Alt img" /></p>

<p>格姆女神山，看到女神山，就离泸沽湖不远了</p>

<p><img src="http://yaodanzhang.com/images/800/4-22.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/4-24.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/4-25.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/4-27.jpg" alt="Alt img" /></p>

<p>这是我们和泸沽湖初次见面的地方，汽车在女神峰旁开了许久，然后绕过一个湾，然后，泸沽湖就这样出现在我们眼前，没有半点犹豫，也没有犹抱琵琶半遮面的含蓄，就这样把她的美毫无保留地展露在完全没有心理准备的我们面前，这是足以震撼人心的美，是用什么语言来形容都无法表现出来的美。</p>

<p><img src="http://yaodanzhang.com/images/800/4-26.jpg" alt="Alt img" /></p>

<p>这个露台是泸沽湖最好的几个观景台之一，基本上来这里的游客下车后的第一件事就是尖叫，好吧，我承认我也尖叫了。。。。。</p>

<p>传说中的里格村就在这个观景台下面，这里是泸沽湖风景最好的村子之一。里格的客栈很多，这个时候正是淡季，而且我们在里格村子里也没看到多少游人，但诡异的是几乎家家都客满，令人很费解。我们几个最终分开在两家客栈住的，分别是老谢车马店和红尘往事。老谢在丽江就住过了，他家在里格也是最早的几家客栈之一，很有特色。红尘往事是一家小旅店，各种设施都不行，价格也便宜，就不评论了。</p>

<p><img src="http://yaodanzhang.com/images/800/4-31.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/4-30.jpg" alt="Alt img" /></p>

<p>泸沽湖的美景极大的刺激了我们一群人的消费欲，订好了当天晚上的客栈后就前往里格半岛，准备奢侈一把，接下来两晚就住里格岛的湖景房了。</p>

<p><img src="http://yaodanzhang.com/images/800/4-28-1.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/4-28.jpg" alt="Alt img" /></p>

<p>这个小姑娘超级有镜头感的</p>

<p><img src="http://yaodanzhang.com/images/800/4-33.jpg" alt="Alt img" /></p>

<p>里格湾的湖景1</p>

<p><img src="http://yaodanzhang.com/images/800/4-41.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/4-42.jpg" alt="Alt img" /></p>

<p>里格湾的湖景2（对面就是里格岛）</p>

<p><img src="http://yaodanzhang.com/images/800/4-38.jpg" alt="Alt img" /></p>

<p>里格湾一家很有特色的客栈</p>

<p><img src="http://yaodanzhang.com/images/800/4-44.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/4-46.jpg" alt="Alt img" /></p>

<p>里格岛东岸</p>

<p><img src="http://yaodanzhang.com/images/800/4-48.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/4-45.jpg" alt="Alt img" /></p>

<p>大名鼎鼎的半岛7号</p>

<p><img src="http://yaodanzhang.com/images/800/4-49.jpg" alt="Alt img" /></p>

<p>里格岛上的客栈都很有特色</p>

<p><img src="http://yaodanzhang.com/images/800/4-44-1.jpg" alt="Alt img" /></p>

<p>里格岛不大，岛上一共只有5家客栈，我们最终住的是岛入口处的“达吧旅行者之家”。</p>

<p><img src="http://yaodanzhang.com/images/800/4-37.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/4-43.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/4-47.jpg" alt="Alt img" /></p>

<p>里格岛上看日落</p>

<p>在泸沽湖，吃饭是个大问题，这里除了烧烤，几乎没有别的。第一天晚上我们在一家挺有名的烧烤店吃的，我们几人的口都挺清淡的，所以没太吃的惯。</p>

<p><img src="http://yaodanzhang.com/images/800/4-50.jpg" alt="Alt img" /></p>

<p>可怜的蘑菇</p>

<p><img src="http://yaodanzhang.com/images/800/4-61.jpg" alt="Alt img" /></p>

<p>在泸沽湖的第二天，我们睡了个懒觉，起床就进驻了湖景房，大赞。</p>

<p><img src="http://yaodanzhang.com/images/800/4-51.jpg" alt="Alt img" /></p>

<p>里格湾的全景神拍</p>

<p><img src="http://yaodanzhang.com/images/800/4-52.jpg" alt="Alt img" /></p>

<p>可能泸沽湖游人没有翠湖多，泸沽湖里的海鸥（不确定是那种品种，姑且这么叫吧）们还没练就一身空中夺饼干的好本领。</p>

<p><img src="http://yaodanzhang.com/images/800/4-55-3.jpg" alt="Alt img" /></p>

<p>湖中的野鸭很酷，根本不屌我们给的饼干，他们的食物是水中的小鱼，潜水下去，叼着小鱼上来，几乎每潜必中，很是厉害。</p>

<p><img src="http://yaodanzhang.com/images/800/4-53.jpg" alt="Alt img" /></p>

<p>1600一夜的宾馆</p>

<p><img src="http://yaodanzhang.com/images/800/4-55.jpg" alt="Alt img" /></p>

<p>传说中的彼岸客栈</p>

<p><img src="http://yaodanzhang.com/images/800/4-55-1.jpg" alt="Alt img" /></p>

<p>专业船夫</p>

<p><img src="http://yaodanzhang.com/images/800/4-55-2.jpg" alt="Alt img" /></p>

<p>一船豪爽的川妹子</p>

<p><img src="http://yaodanzhang.com/images/800/4-36.jpg" alt="Alt img" /></p>

<p>划船回来，我们参观了船主的家。船主家在里格湾开了一家客栈：“里格假日酒店”，是当地最有钱的人家之一。</p>

<p><img src="http://yaodanzhang.com/images/800/4-56.jpg" alt="Alt img" /></p>

<p>这是主人家相对现代化的客厅，客厅中间挂着女主人年轻时的照片，这些照片是当年一位四川美院的教授给她拍的，真的是个大美女啊！</p>

<p><img src="http://yaodanzhang.com/images/800/4-55-5.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/4-55-6.jpg" alt="Alt img" /></p>

<p>祖母屋。祖母屋和经堂是摩梭人传统建筑中最重要的两间屋子，祖母屋是是家中最年长的女性（也就是一家之主）居住的地方；经堂不让拍照，是供奉佛像和举行宗教仪式的地方。</p>

<p><img src="http://yaodanzhang.com/images/800/4-55-4.jpg" alt="Alt img" /></p>

<p>猪膘肉，放个七八年就可以生吃了</p>

<p><img src="http://yaodanzhang.com/images/800/4-57.jpg" alt="Alt img" /></p>

<p>这里的黑猪都是放养的</p>

<p><img src="http://yaodanzhang.com/images/800/4-59.jpg" alt="Alt img" /></p>

<p>防火防盗防黑店：“二车拉葱”。</p>

<p><img src="http://yaodanzhang.com/images/800/4-58.jpg" alt="Alt img" /></p>

<p>面罩+墨镜=拍照神器</p>

<p><img src="http://yaodanzhang.com/images/800/4-63.jpg" alt="Alt img" /></p>

<p>里格村岸边的全景神拍</p>

<p><img src="http://yaodanzhang.com/images/800/4-65.jpg" alt="Alt img" /></p>

<p>阳光下的彩虹，生平第一次看到</p>

<p><img src="http://yaodanzhang.com/images/800/4-61-1.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/4-78.jpg" alt="Alt img" /></p>

<p>下午回到客栈，客栈的一楼大堂是老板开的酒吧，四周都是大大的落地窗，晚上有歌手驻唱。在酒吧里，我们几个写明信片、看书、拍照、发微博，晒太阳，就这么过了一下午。</p>

<p><img src="http://yaodanzhang.com/images/800/4-72.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/4-62.jpg" alt="Alt img" /></p>

<p>从阳台上看下去，湖水那个透啊。</p>

<p><img src="http://yaodanzhang.com/images/800/4-66.jpg" alt="Alt img" /></p>

<p>达吧卖的明信片很不错</p>

<p><img src="http://yaodanzhang.com/images/800/4-67.jpg" alt="Alt img" /></p>

<p>无节操的老板</p>

<p><img src="http://yaodanzhang.com/images/800/4-71.jpg" alt="Alt img" /></p>

<p>听客栈老板达吧的推荐，我们晚餐去了村里停车场附近的一家川菜馆，这里的饭菜还算不错，尤其对于口味清单或者不想每顿都吃烧烤的筒子们来说，是一个很不错的地方。我们接下来几天都在这里解决的。</p>

<p><img src="http://yaodanzhang.com/images/800/4-68.jpg" alt="Alt img" /></p>

<p>这个小伙儿住在附近的村子里，每天到里格来卖各种干货，苹果干、松茸干之类。很欢乐的一个小伙，而且英语说的特别好，我跟他正聊着，又过来俩白人，这哥们儿马上用一口地道的英国伦敦腔跟他们聊了起来，说的我自惭形秽。</p>

<p><img src="http://yaodanzhang.com/images/800/4-70.jpg" alt="Alt img" /></p>

<p>这是他卖的苹果干，味道很不错</p>

<p><img src="http://yaodanzhang.com/images/800/4-69.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/4-73-1.jpg" alt="Alt img" /></p>

<p>晚上，我们在客栈的酒吧玩了一宿，酒吧大家都在安静的听歌，不太吵，气氛很好。</p>

<p><img src="http://yaodanzhang.com/images/800/4-73.jpg" alt="Alt img" /></p>

<p>唱歌的是长期在这儿驻唱的歌手，他旁边打手鼓的是这个客栈的老板达吧。这个驻唱歌手类型不是我喜欢的，但是老板达吧的手鼓打的非常好，大家有机会一定要去听一下。</p>

<p><img src="http://yaodanzhang.com/images/800/4-74.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/4-75.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/4-76.jpg" alt="Alt img" /></p>

<p>在泸沽湖的第三天，我们早早的就起来在阳台上等着看日出。泸沽湖的清晨很冷，我们裹着毯子、吹着风等了2个多小时，无奈这天云层太厚，什么都没看到。</p>

<p><a href="http://weibo.com/meikidd">@meikidd</a>同学在阳台上等日出的时候，楼下过来一群划船的美女，他们和船主唱了会儿山歌后，划到我们楼下，就开始讨论<a href="http://weibo.com/meikidd">@meikidd</a>，这是她们的原话：“阳台上这个肯定不是富二代就是官二代，年纪轻轻就这么会享受，能住湖景房，还一大早起来就在湖边晒太阳看日出”。哈哈，<a href="http://weibo.com/meikidd">@meikidd</a>你以后不用这么低调了，无论你在什么地方，就算别人在湖里你在岸上，你身上高富帅的气质，都像漆黑中的萤火虫一样，那样的鲜明，那样的出众。</p>

<p><img src="http://yaodanzhang.com/images/800/4-60.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/4-60-1.jpg" alt="Alt img" /></p>

<p>高富帅</p>

<p>我们白天最主要的任务就是拍照，各种自拍，合拍，偷拍。。。。。</p>

<p><img src="http://yaodanzhang.com/images/800/4-77.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/4-79.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/4-80.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/4-81.jpg" alt="Alt img" /></p>

<p>知道我们在干嘛吗？看看下图：</p>

<p><img src="http://yaodanzhang.com/images/800/4-81-2.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/4-83.jpg" alt="Alt img" /></p>

<p><a href="http://weibo.com/meikidd">@meikidd</a>，你的脑袋是P的呢还是P的呢还是P的呢？</p>

<p>吃完午饭，我们在酒吧待了会儿，就联系木师父，把我们送去女神湾。</p>

<p><img src="http://yaodanzhang.com/images/800/4-85.jpg" alt="Alt img" /></p>

<p>沿途</p>

<p>女神湾的风景和里格村的风格大不一样，这里比里格的人更少，整个下午几乎只有我6个人；这里视野并不宽阔，却正对女神山，能远眺摩挲神山的全貌；这里十分干净，岸上的小石头就像刚刚洗过一样，没有半点灰尘；这里是整个泸沽湖看日落最好的地方，可惜这天云层太厚，没能看到，算是我们一点小小的遗憾吧。</p>

<p><img src="http://yaodanzhang.com/images/800/4-86.jpg" alt="Alt img" /></p>

<p>女神湾入口</p>

<p><img src="http://yaodanzhang.com/images/800/4-87.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/4-88.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/4-89.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/4-91.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/4-92.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/4-93.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/4-94.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/4-95.jpg" alt="Alt img" /></p>

<p>女神湾正对着女神山</p>

<p><img src="http://yaodanzhang.com/images/800/4-97.jpg" alt="Alt img" /></p>

<p>泸沽湖的水很凉</p>

<p><img src="http://yaodanzhang.com/images/800/4-90.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/4-96.jpg" alt="Alt img" /></p>

<p>女神湾的全景神拍</p>

<p><img src="http://yaodanzhang.com/images/800/4-98.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/4-99.jpg" alt="Alt img" /></p>

<p>里格观景台</p>

<p>再过一晚我们就要离开了，这天晚上我们在房间里玩游戏玩到很晚，都希望在泸沽湖的时光能慢点、再慢点。</p>

<p><img src="http://yaodanzhang.com/images/800/5-1.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/5-2.jpg" alt="Alt img" /></p>

<p>最后一天早上，我们终于看到了泸沽湖的日出，非常漂亮，可惜了，没有好的单反，只能有这样的效果。</p>

<p><img src="http://yaodanzhang.com/images/800/5-3-1.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/5-3-2.jpg" alt="Alt img" /></p>

<p>离开里格前的早餐，荞麦饼，酥油茶</p>

<p><img src="http://yaodanzhang.com/images/800/5-3.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/5-4.jpg" alt="Alt img" /></p>

<p><a href="http://weibo.com/kimismj">@up-up-努力向上的S</a>打水漂的动作好专业！</p>

<p><img src="http://yaodanzhang.com/images/800/5-5.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/5-7.jpg" alt="Alt img" /></p>

<p>我们在湖边散步时发现的小鱼，被晒干了粘在石头上，很可惜没有带回来</p>

<p><img src="http://yaodanzhang.com/images/800/5-6-1.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/5-6-2.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/5-6.jpg" alt="Alt img" /></p>

<p>清澈的湖水</p>

<p><img src="http://yaodanzhang.com/images/800/5-8.jpg" alt="Alt img" /></p>

<p>客栈里的二层小楼，这间房只能搭个梯子上去，是跟走婚有关么？</p>

<p><img src="http://yaodanzhang.com/images/800/5-9.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/5-10.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/5-12.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/5-13.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/5-14.jpg" alt="Alt img" /></p>

<p>五连拍</p>

<p><img src="http://yaodanzhang.com/images/800/5-15.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/5-16.jpg" alt="Alt img" /></p>

<p>偷拍</p>

<p><img src="http://yaodanzhang.com/images/800/5-17.jpg" alt="Alt img" /></p>

<p>落水村全景神拍。从里格离开泸沽湖的路上会途经落水村，这是泸沽湖上另一个风景很好的村子。</p>

<p><img src="http://yaodanzhang.com/images/800/5-19.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/5-20.jpg" alt="Alt img" /></p>

<p>可爱的俩母子</p>

<p><img src="http://yaodanzhang.com/images/800/5-21.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/5-22.jpg" alt="Alt img" /></p>

<p>最后一瞥。这是最后一个能看到泸沽湖的地方，过了这里，转个弯，就彻底和泸沽湖告别了。</p>

<p>从泸沽湖回丽江我们走的另一条线，也就是大巴走的那条线路，路上的景色同我们来的时候那条路的差距实在是太大了，我们几个人干脆在车上玩起了游戏。</p>

<p><img src="http://yaodanzhang.com/images/800/5-23.jpg" alt="Alt img" /></p>

<p>传说中的丽宁十八弯。拍照拍出来很好看，但是实际的景色远远不如我们来的路上那些盘山路。</p>

<p><img src="http://yaodanzhang.com/images/800/5-28.jpg" alt="Alt img" /></p>

<p>玩着游戏，很快我们就回到了丽江古城，这天的天气非常好，傍晚的丽江也是很美的。</p>

<p><img src="http://yaodanzhang.com/images/800/5-25.jpg" alt="Alt img" /></p>

<p>偷拍美女</p>

<p><img src="http://yaodanzhang.com/images/800/5-26.jpg" alt="Alt img" /></p>

<p><img src="http://yaodanzhang.com/images/800/5-27.jpg" alt="Alt img" /></p>

<p>草泥马</p>

<p>3.14日晚，丽江到昆明，火车；</p>

<p>3.15日晨，昆明到重庆/郑州/武汉，回家。</p>

<p><img src="http://yaodanzhang.com/images/800/6-1.jpg" alt="Alt img" /></p>

<p>在泸沽湖的4天，我们彻彻底底的放松了自己，每天的行程很少，除了拍照就是在湖边和酒吧里发呆，没有去环湖，甚至泸沽湖周围的众多景点中，我们只去了里格和女神湾，没有很贪婪的想着一次吧泸沽湖的景点游完，大概大家心里都已经盘算着下次甚至下下次来泸沽湖的计划了，我们很默契的把它们当作下次来的时候给自己的惊喜吧。</p>

<p>“若有一个地方，让你去了便深深依恋，想留下，想老死，那就是泸沽湖！也有人说，不要轻易去泸沽湖，因为她会把你留下。人生最大的遗憾就是，人走了，魂却还在泸沽湖!”</p>

<h1>About 旅行</h1>

<p>文章到最后了，我也不免俗的想跟大家聊聊旅行的意义。</p>

<p>这是一个充满惊喜的旅程，有惊喜的食物、惊喜的行程、惊喜的节目，旅程中最快乐的部分基本都不在预先的计划之中。</p>

<p>计划旅行的时候总想着，要去好多不一样的地方，做好多超凡脱俗的事情。却忘了，自己并不是一个的脱俗的人，我需要的，只是一段可以久久回味的快乐的记忆。而能让我自己快乐的，不是去了什么地方，也不是做了什么事情，而是在毕业的季节，与朋友们一起去认识这些地方、去经历这一切。</p>

<p>我心目中完美的旅行就应该是这样，在合适的时间、找一群亲密的朋友、去一个能让自己心动的地方、做一些能让自己快乐的事情。想骑车了，就去骑，骑上100来公里，哪怕累到吃饭的力气都没有了，我们仍然是快乐的；骑到一半骑不动了，那就停下来，坐车去下一个目的地，用省下来的时间去看看不同的街道，看看不一样的人，同样是快乐的；可以一天去好几个城市，逛街、逛书店、逛教堂、逛博物馆，尽力去开拓自己的眼界，也可以好几天都待在同一个村子，遛狗、看书、自拍、玩微博、晒太阳，努力去做一个懒惰的人、与社会脱节的人、一个低级趣味的人，只要我们是快乐的。不需要什么目标，更没有所谓的坚持，随心而行，率性而为，抛开计划，抛开那束缚自己的一切，去迎接旅途中一个又一个的惊喜。</p>

<p>这样的旅行很简单，仔细想想，我们需要的东西其实也很简单！</p>

<p>最后，感谢朋友们的陪伴，感谢造物主的神奇，感谢摩梭人对泸沽湖的保护，感谢旅程中遇到的所有可爱的人、可爱的动物。</p>

<p>最后的最后，这次去云南，时间其实很充足，却只选择了大理／丽江／泸沽湖这三个地方，有些刻意的避开了雨崩／香格里拉／虎跳峡／梅里雪山等等，<a href="http://weibo.com/meikidd">@meikidd</a>，<a href="http://weibo.com/kimismj">@up-up-努力向上的S</a>，<a href="http://weibo.com/u/1974590105">@只是很无趣</a>，<a href="http://weibo.com/ireneshy">@irene</a>，<a href="http://weibo.com/u/2948037661">@kim1219</a> 这可都是为你们下次年假准备的哦，哈哈！</p>

<p>－－－－－－－－－－－－－－－－－       我是分割线     －－－－－－－－－－－－－－－－－－</p>

<h1>附录</h1>

<h2>衣</h2>

<p>云南除了香格里拉和西双版纳等少数几个地方，一年四级温度都差不多，通常在3 &ndash; 20°C之间。昼夜温差比较大，一般一件短袖或长袖再加一件外套就OK了，泸沽湖要稍微冷一些，最好穿一件薄毛衣。</p>

<h2>食</h2>

<p>云南在东9区，比北京晚一个小时，我们吃饭的时间很不规律，一般午饭在下午2点多，晚饭在晚上8、9点。</p>

<ul>
<li><p>昆明：网上很多说小锅米线比过桥米线好吃，可能我们几个吃的那家店不行，感觉不好吃。博物馆附近的“傣家竹楼”给了我们很大的惊喜，傣族风味儿，大家有机会一定要去尝尝；</p></li>
<li><p>大理：环洱海的路上很多白族民居饭店，白族的口味稍微有点重，但是味道还是很不错的；</p></li>
<li><p>丽江：丽江的物价比别的地方要贵很多，我们就去吃了家过桥米线，还不错。</p></li>
<li><p>泸沽湖：泸沽湖可以选择的吃的很少，当地的特色是烧烤，在里格几乎也只有烧烤（其他地方没去过，不太清楚），实在吃不惯可以尝尝停车场那家川菜馆“满庭香”，味道还可以。</p></li>
</ul>


<p>PS：</p>

<p><img src="http://yaodanzhang.com/images/800/5-24.jpg" alt="Alt img" /></p>

<p>炸竹虫，云南名小吃之一。大家可以尝一尝，哈哈^_^</p>

<h2>住</h2>

<p><img src="http://yaodanzhang.com/images/800/7-1.jpg" alt="Alt img" /></p>

<p>在云南，我们一共住了这几家客栈：海地生活（双廊），春夏秋冬青旅（大理古城），老谢车马店（丽江古城、里格村），达吧旅行者之家（里格半岛），红尘往事（里格村）。</p>

<p>这几个当中，最差的是红尘往事，各种设施不齐全，我们只住了一晚，就不说了。</p>

<p>最好的自然是海地，不知是这里的美感染了来这里的人还是来这里的人真的素质很高，大家都非常自觉的维护着这里。一个很小的例子，我们住的8人间晚上非常安静（除了LZ的呼噜&hellip;Orz&hellip;），大家说话动作都非常轻，生怕吵到了别人，这种感觉太棒了。</p>

<p>达吧旅行者之家除了价格贵一些，别的都很好。里格岛上一共5家客栈，只有这里是地道的摩梭人开的。据说老板达吧曾经被选到北京某歌舞团做演员，到昆明的时候达吧后悔了偷偷地又跑了回来，人长的非常非常帅，而且打得一手好鼓。这里一共10间房，楼上楼下各5间，都是湖景房，风景很赞的。</p>

<p>老谢车马店在丽江和里格有两家店，他家的庭院也挺美的，住青旅的好处就是可以跟来自各地的驴友们交流，共享信息，特别对独自上路的朋友们来说，是很好的。</p>

<p>大理古城的春夏秋冬旅社，我的感觉是很吵，同前一天住的海地差的太多了，房间外面吵，房间里也吵……</p>

<h2>行</h2>

<p>去云南的机票价格取决与大家的时间安排，如果是淡季去的话，提前预定，机票还是很便宜的。</p>

<p>“汽车比火车快”，这是云南十八怪之一。不过现在已经没有这样的问题了。昆明、大理、丽江之间坐火车还是很不错的，能买到软卧的话建议大家买软卧吧，价格不贵，还可以好好的休息一晚。</p>

<p>在大理租自行车的地方很多，复兴路上有好几家，我们在“龙云租车行”租的车，价格不贵，老板人也挺nice的。</p>

<p>重点讲一下丽江到泸沽湖的包车吧。能跑这条路的一般都是越野车，把后备箱改一下加排座位，可以坐6个人，车费一般是600块钱一天，如果行程3天的话，来回各一天，中间一天在泸沽湖可以坐车去环湖，对于时间紧凑的筒子们还是很划算的。如果中间一天不用他的车的话还可以便宜300块钱。我们一共是4天，中间两天在泸沽湖没用车，算300块钱一天，来回两天600/天，一共1800。来回的行车路线可能由司机师傅选择，我们去的那条路的美给我带来的惊喜和震撼是没法用语言形容的。我们包车的木师傅他老婆是丽江古城老谢车马店的服务员，他的手机号是15012241652，大家如果有机会，一定要去走一下这段路。</p>

<h2>娱乐</h2>

<p>在云南娱乐方式不多，我们主要就是看书、发呆、晒太阳、发微博。和朋友们聊天玩游戏也是很不错的，</p>

<h2>关于旅行时间</h2>

<p>相对别的地方，云南一年四季的游人都挺多的，这里的淡季一般是每年的11月到次年的3月（中间除开春节、元旦假期），个人觉得这个时候去最好，人不多却又不会觉得冷清，是静静享受假期最好的时光。</p>

<h2>旅行必备</h2>

<ul>
<li><p>洗漱用品。虽然很多宾馆会提供一次性的，但我还是建议大家带上基本的牙刷牙膏之类，为减少云南的污染做一点小小的努力把。</p></li>
<li><p>外伤用药</p></li>
<li><p>护肤品：防晒霜，个人觉得SPF50可能没有必要，但是30绝对要，虽然是冬天，但云南的紫外线真是名不虚传。唇膏护手霜，尤其是女孩子一定要做好保湿工作，气候非常干燥。</p></li>
<li><p>墨镜：有两个原因，一是拍照神器；二是防晒，云南阳光充足，不戴墨镜一天下来眼睛吃不消。</p></li>
<li><p>遮阳帽：一方面可以挡挡光，一方面也能起到一些御寒的作用，毕竟云南那边温差很大！</p></li>
<li><p>保暖背心：昼夜温差是推荐携带背心的最主要原因，云南各地由于山地的关系，地区和地区之间温差特别大，尤其在泸沽湖这样海拔稍高的地方。</p></li>
<li><p>防滑的徒步鞋，最好也防水：云南气候干燥，山路不好走，植被比较少，路上的土也多成沙粒状，走下坡路尤其要小心滑，一般的旅游鞋无法承担所有的重责，还是专业的徒步/登山鞋比较保险。</p></li>
<li><p>单反：我们这次没带单反（<a href="http://weibo.com/meikidd">@meikidd</a>去面壁一下），拍出来的照片和我们看到的景色差距太大了，很是遗憾。</p></li>
</ul>


<h2>其他建议</h2>

<p>注意环保：云南开发的越来越商业化，游人也越来越多，不可避免的带来很多问题。在旅行的路上，希望大家能稍微为环保做出一点自己的努力，不乱扔垃圾、少用一次性物品，也不会很麻烦是不？</p>
]]></content>
  </entry>
  
</feed>
