预计阅读本页时间:-
31.告知,不要询问
“不要相信其它的对象。毕竟,它们是有别人写的,甚至有可能是你自己上个月头脑发昏的时候写的呢。从别人那里去拿你需要的信息,然后自己处理,自己决策。不要放弃控制别人的机会!”
“面向过程的代码取得信息,然后做出决策。面向对象的代码让别的对象去做事情。”Alec Sharp[Sha97]通过观察后,一针见血地指出了这个关键点。但是这种说法并不仅限于面向对象的开发,任何敏捷的代码都应该遵循这个方式。
作为某段代码的调用者,开发人员绝对不应该基于被调用对象的状态来做出任何决策,更不能去改变该对象的状态。这样的逻辑应该是被调用对象的责任,而不是你的。在该对象之外替它做决策,就违反了它的封装原则,而且为bug提供了滋生的土壤。
广告:个人专属 VPN,独立 IP,无限流量,多机房切换,还可以屏蔽广告和恶意软件,每月最低仅 5 美元
David Bock使用“送报男孩和钱包的故事”很好地诠释了这一点。假定送报男孩来到你的门前,要求付给他本周的报酬。你转过身去,让送报男孩从你的后屁股兜里掏出钱包,并且从中拿走两美元(你希望是这么多),在把钱包放回去。然后,送报男孩就会开着他崭新的美洲豹汽车扬长而去了。
在这个过程中,送报男孩作为“调用者”,应该告诉客户付他两美元。他不能探询客户的财务状况,或是 将命令与查询分离开来钱包的薄厚,他也不能代替客户做任何决策。这都是 Keep commands separate from queries客户的责任,而不属于送报男孩。敏捷代码也应该以同样的方式工作。
与告知,不要询问相关的一个很有用的技术是:命令与查询相分离模式(command-query separation)。就是要将功能和方法分为“命令”和“查询”两类,并在源码中纪录下来(这样做可以帮助将所有的“命令”代码放在一起,并将所有的“查询”代码放在一起)。
一个常规的“命令”可能会改变对象的状态,而且有可能返回一些有用的值,以方便使用.一个“查询”仅仅提供给开发人员对象的状态,并不会对其外部的可见状态进行修改.
这就是说,从外部看来,“查询”不应该有任何副作用(如果需要的话,开发人员可能想在后台做一些事先的计算或是缓存处理,但是取得对象中X的值,不应该改变Y的值).
像“命令”这种会产生内部影响的方法,强化了告知,不要询问的建议.此外保证“查询”没有副作用,也是很好的编码实践,因为开发人员可以在单元测试中自由使它们,在不断言或者调试器中调用它们,而不会改变应用的状态.
从外部将“查询”与“命令”隔离开来,还会给开发人员机会询问自己为什么要暴露某些特定的数据.真的需要这么做吗?调用者会如何使用它? 也许应该有一个相关的“名列”来替代它.
告知,不要询问。不要抢别的对象或是组件的工作。告诉它做什么,然后盯着你自己的职责就好了。
切身感受
Smalltalk使用“信息传递”的概念,而不是方法调用。告知,不要询问感觉起来就像你的发送消息,而不是调用函数。
平衡的艺术
□ 一个对象,如果只有用作大量数据容器,这样的做法很可疑,有些情况不会需要这样的东芝,但并不像想象的那么频繁。
□ 一个“命令”返回数据以方便使用是没有问题的(如果需要的话,创建单独读取数据的方法也是可以的)。
□ 绝对不能允许一个看起来无辜的“查询”去修改对象的状态。