命令,不要去询问 (Tell, Don’t Ask)

openkk 12年前
   <p>前些时间我曾经翻译过一篇叫做《<a href="/misc/goto?guid=4958521627356060877" rel="nofollow">这里我说了算!</a>》的文章,里面作者讲述了关于“命令,不要去询问(Tell, Don’t Ask)”原则:</p>    <blockquote>     <p>我看到的最多被违反的原则是“命令,不要去询问(Tell, Don’t Ask)”原则。这个原则讲的是,一个对象应该命令其它对象该做什么,而不是去查询其它对象的状态来决定做什么(查询其它对象的状态来决定做什么也被称作 ‘功能嫉妒(Feature Envy)’)。</p>    </blockquote>    <p>这篇文章里有个很生动的例子,我至今记忆犹新:</p>    <blockquote>     <p>if (person.getAddress().getCountry() == “Australia”) {</p>     <p>这违反了得墨忒耳定律,因为这个调用者跟Person过于亲密。它知道Person里有一个Address,而Address里还有一个country。它实际上应该写成这样:</p>     <p>if (person.livesIn(“Australia”)) {</p>    </blockquote>    <p>非常的明了。今天我又看到一个关于“Tell, Don’t Ask”原则的文章,里面提供了4个关于这个原则的例子,都很有价值。</p>    <h2>例一</h2>    <p>不好:</p>    <pre class="brush:ruby; toolbar: true; auto-links: false;"><% if current_user.admin? %>    <%= current_user.admin_welcome_message %>  <% else %>    <%= current_user.user_welcome_message %>    <% end %></pre>    <p>好:</p>    <pre class="brush:ruby; toolbar: true; auto-links: false;"><%= current_user.welcome_message %></pre>    <h2>例二</h2>    <p>不好:</p>    <pre class="brush:ruby; toolbar: true; auto-links: false;">def check_for_overheating(system_monitor)      if system_monitor.temperature > 100      system_monitor.sound_alarms    end    end</pre>    <p>好:</p>    <pre class="brush:ruby; toolbar: true; auto-links: false;">system_monitor.check_for_overheating    class SystemMonitor    def check_for_overheating        if temperature > 100        sound_alarms      end    end    end</pre>    <h2>例三</h2>    <p>不好:</p>    <pre class="brush:ruby; toolbar: true; auto-links: false;">class Post    def send_to_feed        if user.is_a?(推terUser)        user.send_to_feed(contents)      end      end  end</pre>    <p>好:</p>    <pre class="brush:ruby; toolbar: true; auto-links: false;">class Post    def send_to_feed        user.send_to_feed(contents)    end  end    class 推terUser    def send_to_feed(contents)        推ter_client.post_to_feed(contents)    end  end    class EmailUser    def send_to_feed(contents)        # no-op.    end  end</pre>    <h2>例四</h2>    <p>不好:</p>    <pre class="brush:ruby; toolbar: true; auto-links: false;">def street_name(user)      if user.address      user.address.street_name    else        'No street name on file'    end  end</pre>    <p>好:</p>    <pre class="brush:ruby; toolbar: true; auto-links: false;">def street_name(user)      user.address.street_name  end    class User    def address        @address || NullAddress.new    end  end    class NullAddress      def street_name      'No street name on file'    end  end</pre>    <p>好的面向对象编程是告诉对象你要做什么,而不是询问对象的状态后根据状态做行动。数据和依赖这些数据的操作都应该属于同一个对象。</p>    <p>命令,不要去询问!</p>    <p>[本文英文原文链接:<a href="/misc/goto?guid=4958521627446527782" rel="nofollow">Tell, Don’t Ask</a> / 译文:<a href="/misc/goto?guid=4958521627543860487" rel="nofollow" target="_blank">外刊IT评论</a>] </p>