[本文已经作为垃圾,请参考什么是好莱坞原则]
如果网上搜索依赖倒置原理/DIP、控制反转(IoC)容器等,会得到一大堆的信息,其中可能还包括yqj2065在本博客中发布的The Dependency Inversion Principle(翻译)(2005-01-28 00:22)的转载版(原文我也移到了垃圾桶)。
最近,在我系统地回顾设计模式时,感到应该与DIP、IoC说ByeBye了。
1、“依赖倒置”(1)这个名字令人不舒服,(2)当用“好莱坞法则”解释它时,显得不伦不类。(3)逻辑混乱,被我抛弃。
2、IoC,被玩坏了。
1.回调机制和好莱坞法则
- 回调(call back,一个动词词组)机制,是一门编程语言,使得下层模块/库可以调用或执行上层模块定义的代码的机制。上层模块所定义的、被(下层模块)调用或动态绑定的代码,则被称为回调函数 (简称回调、callback,一个名词)。
- 回调机制用于框架设计和通知机制。
通知机制中,如图9-11所示,上层模块Client调用了下层Server的copy()方法。假设上层需要更新进度条——显示复制任务完成的进度,显然上层并不清楚复制的进度而只有下层的Server才知道。这时下层模块如何将进度数据传递给上层的Client呢?
一种简单的方式:Server将进度数据保存在一个成员变量x中,并提供getX()。这样就需要Client时时刻刻地查询该数据。如同现实生活中打的士到某地,从上车的第一秒开始,时刻或每隔5秒问司机到了地方没有,也太烦人了。
另一种方式则是通知或使用回调。
2. 好莱坞法则:多个Client的对象或多个IXxx的其他实现类对下层Server2对象的某种状态变化感兴趣。以现实生活中的场景为例:一些男女演员们(Client)都对某导演(Server2)是否拍新片子感兴趣。导演肯定和的士司机一样,不喜欢演员们天天打电话询问。于是导演提供了一个让感兴趣的演员们留下电话号码的接口register(IXxx listener)。一旦导演准备拍摄一部新片子(sthHappened())就通知所有已登记的演员。而对于那些随时打电话询问是否有戏的演员,导演告诉他们一条好莱坞法则:"Don't call me; I'll call you."。
2.两个结构
这是OCP/抽象依赖原则的类图。
图1 抽象依赖原则
这是回调/好莱坞/观察者模式的类图:
图2 回调/好莱坞法则
两个结构完全相同的图,却有完全不同的含义/语义。
- DIP的类图中,Client可以随时随地地调用Server,但是地主应该依赖于“I丫鬟”;
- 回调/好莱坞/观察者模式的类图中,绝对不允许S依赖C。
用好莱坞法则、钩子解释OCP、DIP、模板方法显得异常地别致。
3.控制反转(IoC)
DIP的描述除了有点含糊,名字不好之外,可以作为OCP的操作性的注解。而控制反转(IoC)则是DIP缺点的继承者。
- 对于图1,我不能够理解IoC的含义,地主依赖一个丫鬟,变成地主依赖一个I丫鬟,这时地主在编译时不依赖具体的丫鬟,但是地主还是依赖丫鬟。(注:有人用层次结构说明DIP的意义,有所谓真假DIP,等下在说。)
《设计模式》、和[3P•11DIP]中,出现了别致到别扭的好莱坞法则的解读。地主要I丫鬟上茶(),
I丫鬟 yh = new 丫鬟(); // I丫鬟 yh =(I丫鬟)getObject();
yh.上茶();
按照我们正常人的说法:yh按照引用的实际类型,动态绑定方法体。
看看别致的说法:地主要I丫鬟上茶,I丫鬟调用子类型丫鬟的上茶()。本来子类依赖于父类,这里父类可以调用子类型的方法,所以……父类依赖于子类??我不知道这里在说什么。
- 《设计模式》:“模板方法导致一种反向的控制结构,这种结构有时被称为“好莱坞法则” ,即“别找我们,我们找你”[ S w e 8 5 ]。这指的是一个父类调用一个子类的操作,而不是相反。”
- [3P•11DIP]:“This is sometimes known as the Hollywood principle..... The lower-level modules privide the implementation for interfaces that are declared within,andcalled by,the upper-level modules”
“反转”总不会是说父类依赖于子类吧?
- 对于图2,也不知道哪里反转了。再说了,有回调或者观察者模式,这个含含糊糊的控制反转(IoC)纯属多余。
总之,IoC要么别扭,要么多余。
4.抽象依赖原则
一、在类设计层面,OCP、针对接口编程,或者我喜欢使用的“抽象依赖原则”(或许包括DIP)是同义词。OCP是目的/目标性的,针对接口编程,“抽象依赖原则”,DIP是操作性的。
但是DIP要改:
第一条: 地主 should not depend upon 丫鬟, both should depend upon I丫鬟。
第二条:不知道在类设计层面,这个Abstractions的意思。如果是指I丫鬟,这就是废话;如果是指Client?这就是梦话。ok,在类设计层面,这一条不要。
二、在架构设计或分层设计中,
上下层中添加一个服务层(接口层),可以看成DIP的应用,也是SOA的常用模式,【与《企业应用架构模式》中的Service Layer模式有些不同】。所以DIP-1挺好的。但是“倒置”,我随时不适应。依赖接口层的目的是封装变化,至于接口层与上层打包(所谓真DIP),还是与下层打包,有真正的价值和实用意义吗?
总之,DIP除了“倒置”和需要修改外,还是好的。在有OCP、针对接口编程,“抽象依赖原则”时,就显得多余。特别是在类设计层面,完全辞不达意。