Ask员工类型, 根据类型选择相应的处理逻辑, 这就是以上的方案,可以看出这不是一个好的解决办法. 好的方法是什么呢? 既然我们要根据员工的类型来判断采用何种计算方法, 而员工显然知道自己是何种类型以及自己的工作量, 那么为什么不交给员工自己来算呢?(具体实现的时候就会涉及到多态等等方法, 这不是本文讨论的重点,在此不做详细介绍)那这个时候会计部干什么呢? 计算工资这个活动总要有人发起, 所以会计部现在的工作就是Tell所有的员工, 让大家计算工资,然后把结果汇总.
现在你可以看到Don’t Ask, Tell的影子了吧.
Ask 带来的坏处:
-
1. 破坏对象的封装性, 你不得不暴露很多的属性供别人Ask.
-
2. 容易使得一些对象过于复杂(会计部), 而一些对象(员工)又过于简单, 甚至成为了仅仅包含数据的”哑”对象.
Tell 带来的好处:
-
1. 更多的考虑责任分配的合理性, 方法涉及的数据在哪里方法就应该在哪里. 这样对象的内聚性就大大加强了.
-
2. 增强了对象的封装性, 对象不必暴露更多的属性.
-
3. 可以充分发挥面向对象的特性,比如多态, 减少”哑”对象从而获得更强的可维护性,扩展性以及面对变化的能力.
上面那个计算工资的例子可以说是被用滥了, 再来看看在其他场合如何运用Don’t Ask, Tell的思想. 集合的遍历操作, 在日常的编程中屡见不鲜. 你是否考虑过它也在一定程度上破坏了Don’t Ask, Tell原则呢? We ask for every element in Collection, then operator on it. Why not just tell the collection to do something. 如果采用ask的办法, 在我们的程序中将不断的出现遍历集合的操作. (重复代码,Bad Smell) 所以我们应该将尽量将集合遍历的操作放在集合内即Refactory away External Loops. 如果你留意了.Net2.0对集合类的最新支持ForEach,你就会发现MS也考虑到了这点.
{
static void Main(string[] args)
{
List<string> strs = new List<string>();
strs.Add("hello");
strs.Add("world");
strs.ForEach(Console.WriteLine);
}
}
不过如我在.Net2.0的集合操作 --- What i hope?一文中提到, ForEach似乎考虑的还不是非常完善.
当然事情没有绝对, 有人会问究竟Tell到什么程度呢? 是不是Money还要负责自己的兑换操作? 这就看你把汇率放哪了. 如果汇率也在Money中, 那Money可以提供兑换的功能, 但是可以看出这不是一个好的设计. 汇率或许应该在Bank对象中, 那Money的兑换操作还是放在Bank中比较合适. 这里就涉及到责任分配的问题.
Don’t Ask, Tell可以说是面向对象中一个非常重要的原则, 这里只是对其简单介绍,其实它还隐藏了很多重要的思想, 见Enterprise Test Driven Develop一文.