前些时间我曾经翻译过一篇叫做《这里我说了算!》的文章,里面作者讲述了关于“命令,不要去询问(Tell, Don’t Ask)”原则:
我看到的最多被违反的原则是“命令,不要去询问(Tell, Don’t Ask)”原则。这个原则讲的是,一个对象应该命令其它对象该做什么,而不是去查询其它对象的状态来决定做什么(查询其它对象的状态来决定做什么也被称作‘功能嫉妒(Feature Envy)’)。
这篇文章里有个很生动的例子,我至今记忆犹新:
if (person.getAddress().getCountry() == “Australia”) {
这违反了得墨忒耳定律,因为这个调用者跟Person过于亲密。它知道Person里有一个Address,而Address里还有一个country。它实际上应该写成这样:
if (person.livesIn(“Australia”)) {
非常的明了。今天我又看到一个关于“Tell, Don’t Ask”原则的文章,里面提供了4个关于这个原则的例子,都很有价值。
例一
不好:
<% if current_user.admin? %> <%= current_user.admin_welcome_message %> <% else %> <%= current_user.user_welcome_message %> <% end %>
好:
<%= current_user.welcome_message %>
例二
不好:
def check_for_overheating(system_monitor) if system_monitor.temperature > 100 system_monitor.sound_alarms end end
好:
system_monitor.check_for_overheating class SystemMonitor def check_for_overheating if temperature > 100 sound_alarms end end end
例三
不好:
class Post def send_to_feed if user.is_a?(TwitterUser) user.send_to_feed(contents) end end end
好:
class Post def send_to_feed user.send_to_feed(contents) end end class TwitterUser def send_to_feed(contents) twitter_client.post_to_feed(contents) end end class EmailUser def send_to_feed(contents) # no-op. end end
例四
不好:
def street_name(user) if user.address user.address.street_name else 'No street name on file' end end
好:
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
好的面向对象编程是告诉对象你要做什么,而不是询问对象的状态后根据状态做行动。数据和依赖这些数据的操作都应该属于同一个对象。
命令,不要去询问!
[英文原文:Tell, Don’t Ask ]
可是我有个问题,如果if (person.livesIn(“Australia”)) 方法里面封装的就是person.getAddress().getCountry() == “Australia” 那后者的存在不应该被判为错误的存在吧。对后者再次进行判断封装,也能接受吧?或许我没看懂原文,请指教
这个例子的意思是:以高层接口(适合语义的接口)进行工作,是“以接口进行工作”的升华
不依赖于具体类 而依赖于接口 或者 代理~
老美工作有个最大的特点就是懂得自保。
如果整个系统都是一个人开发的那随便怎么写都没关系。如果是多个人开发,并且模块间关联较大的话这样做就相当有用了。
一般老美开发软件详细设计也不会做的很好,这样写代码责任就能分得很清楚。至少能解决很多类似“我是这里是调那个人的接口,谁知道他会传给我个NULL!?应该改代码的是他!”这种问题。
副标题可以加上——“怎样减少if的使用”,或者“用多态代替if”
有那么点意思,之前在编写大项目的时候,也经常被这些if烦恼
其实也就那么回事。A类call B类,查询状态,会导致两个类在状态上耦合,但是可以把业务逻辑推到A中。不查询状态直接调用行为,两个类在状态上解耦了,但是会造成在行为上的耦合。世界上哪有什么一刀切的标准,还是要分析具体的业务场景,看看究竟是状态更稳定一些,还是行为更稳定一些,把稳定的方面暴露出去,把变化的方面封装起来。
好像就是:降低耦合
这样的代码更加有层次感,上层对象不用关系太多的底层细节。
实质是要重视每个对象的API设计,对于客户类来讲,从自己的视角去和别的对象通讯,不必知道的,就不应该知道。
正解。
做好类的接口设计和职责划分。
和if多少没有关系。
java的对象模型里,没有行为、只有属性的实体很常见。所以ask也很常见。
鸡毛蒜皮。
其实就是一个最基础的概念—封装,编程学习过程就是这样,很早就知道的概念总是在学习,练习提升后恍然大悟。