您的位置:首页 > 编程语言 > Java开发

《编程导论(Java)·9.3.1回调·2》什么是好莱坞法则

2016-06-25 12:42 288 查看
本文介绍框架、控制反转IoC、好莱坞法则、回调函数、观察者模式等一系列概念的关系。

1.框架与库函数

想象你是下层模块(比如JDK)的设计者,你会编写两种代码:框架与库函数。框架(framework)的基本特点是给应用程序员扩展(extending)

因此,框架与库函数两者的区别,被称为控制反转IoC,也被有些人称为好莱坞法则!This phenomenon is Inversion
of Control (also known as the Hollywood Principle - "Don't call us, we'll call you").Martin Fowler

等式1: 控制反转IoC= (借用的)好莱坞法则

有一个问题:框架(framework)是什么?
《编程导论(Java)·9.1 applet》:为了解决某一类型的问题,设计出一个软件包而提供骨架式的解决方案,则该软件包称为框架(framework)。
[GoF·1.6.7 设计应支持变化]:一个应用经常会使用来自一个或多个被称为工具箱( To o l k i t )的预定义类库中的类。工具箱是一组相关的、可复用的类的集合,这些类提供了通用的功能。框架( framework)是构成一类特定软件可复用设计的一组相互协作的类 [ D e u 8 9 , J F 8 8 ]。框架规定了你的应用的体系结构。它定义了整体结构,类和对象的分割,各部分的主要责任,类和对象怎么协作,以及控制流程。框架预定义了这些设计参数,以便于应用设计者或实现者能集中精力于应用本身的特定细节。框架记录了其应用领域的共同的设计决策。因而框架更强调设计复用,尽管框架常包括具体的立即可用的子类。这个层次的复用导致了应用和它所基于的软件之间的反向控制
(inversion of control)。
上面的我们懂。但是,概念说得天花乱坠没有用,要给一个落地的例子。其实,我们的问题是:

(1)java.applet.Applet是框架吗?好吧,我知道有人说:“java.applet.Applet是小应用程序框架的一份子”。那么,“小应用程序框架是什么?”(给我一个边界,或者说看得见的东西,指java.applet包、java.awt包还是什么?)

(2)java.util.Arrays,一见util就想到工具箱或库函数。但是,其中的public static <T> void sort(T[] a, Comparator<? super T> c)

是框架吗?应用程序员编写的Comparator子类,能不能感受到控制反转IoC?的确,yqj2065难以回答这样的问题。所以,给应用程序员扩展(extending)的下层模块都叫着框架。或者说,体现控制反转的代码都叫框架。
(所以,java.applet.Applet是框架;java.util.Arrays一般作为工具箱,但是sort(T[] a, Comparator<? super T> c)是框架。虽然这种框架的定义,给人的感觉可能不太好,但是体现控制反转的代码,是不是非要通过类的多少或使用了类似Java Event Dispatch很多底层的东西才能够称为框架?这些东西并不是框架的定义中应该的特性。参见最简单的Java框架。)

按照实用主义的框架(framework)概念,以排序方法为例:

package java.util;
//import 略
@FunctionalInterface
public interface Comparator<T> {
int compare(T o1, T o2);
}

框架中定义的、被上层代码override的方法,称为回调接口。而应用程序员编写的回调接口的实现方法叫回调函数/回调。见《编程导论(Java)·9.3.1回调》什么是回调

package com.xyz;
import java.util.Comparator;
public class XxxComparator implements Comparator {
@Override public final int compare(Object pFirst, Object pSecond) {
return 0;
}
}
于是,我们得到了一个实用主义的框架概念:

定义了回调接口的模块是框架。回调机制是框架设计的基本特征。

框架的基本特征是 控制反转IoC= (原始的)好莱坞法则,框架设计的基本特征是回调机制。(有人愿意区别框架表现的特点和设计框架的基本技术吗?不咬文嚼字的话,
等式2:控制反转IoC= (借用的)好莱坞法则 = 回调机制 = 编写回调(函数)=填空式编程

2.好莱坞法则

控制反转IoC,描述一个故事/现象,它不是原理、道理....。某个猿人结婚前是奴隶,结婚后是将军,所以称(这种变化)为从奴隶到将军,所以称为IoC。(借用的)好莱坞法则 =IoC,所以也是讲故事。

但是,要用代码介绍好莱坞法则的时候,不能够讲故事,代码涉及到具体的类。比如说"Don't call me; I'll call you."   

讲故事的时候,me指框架,you指应用。应用程序不要调用框架,框架调用应用程序的代码

用代码介绍好莱坞法则,涉及到具体的类。这里,yqj2065与很多人出现了概念分歧。以上面的排序为例。

package com.xyz;
import java.util.Arrays;
public class ArraySortDemo {
public void sortIntArray() {
int[] arrayToSort = new int[] { 48, 5, 89, 80, 81, 23, 45, 16, 2 };

Arrays.sort(arrayToSort);
}
public void sortObjectArray() {
//Dog o1 =
Dog[] dogs = new Dog[] { o1, o2, o3, o4, o5 };
Arrays.sort(dogs, new XxxComparator());

}
}
应用程序com.xyz.ArraySortDemo、XxxComparator,谁是好莱坞法则中的you

 [设计模式]:“模板方法导致一种反向的控制结构,这种结构有时被称为“好莱坞法则” ,即“别找我们,我们找你”[ S w e 8 5 ]。这指的是一个父类调用一个子类的操作,而不是相反。”GoF的好莱坞法则,父类是me,子类是you,“一个父类调用一个子类的操作”。吐槽版什么是好莱坞法则中,我说明,有动态绑定,“我不喜欢对(父子类关系)的其他云山雾绕的解释!”

[Swe85]:Don't call us, we'll call you (Hollywood's Law): A tool should arrange for Tajo to notify it when the user wishes to communicate someeventto the tool, rather than
adopt an 'ask the user for a command and execute it' model."这也是我采用的好莱坞法则,好莱坞原则的核心:以通知替代轮询

等式3:好莱坞原则 = 通知机制 = 观察者模式

下层模块(比如JDK)的设计者,你会编写两种代码:框架与库函数。应用程序(上层模块)调用(作为库函数的)java.util.Arrays天经地义,包括sortObjectArray()中调用Arrays的sort(T[] a, Comparator<? super T> c) ,这时,我会解释:实参new XxxComparator()为其父类提供了抽象方法int compare(T o1, T o2)的实现代码,动态绑定该代码。【我不会说:Arrays调用Comparator的抽象方法compare(),而父类调用一个子类(上层模块)XxxComparator的代码。】对于仅需要上层模块提供代码的情况,Comparator定义回调接口,应用程序员编写的XxxComparator提供回调函数/回调。这里,控制反转IoC/回调已经与好莱坞原则分道扬镳

我的好莱坞法则,特点的注册+通知。

以现实生活中的场景为例:一些男女演员们(Client)都对某导演(Server2)是否拍新片子感兴趣。导演不喜欢演员们天天打电话询问。于是导演提供了一个让感兴趣的演员们留下电话号码的接口register(IXxx listener),演员工会TestCallback2组织大家登记。一旦导演准备拍摄一部新片子(sthHappened())就通知所有已登记的演员。而对于那些打电话询问的演员,导演告诉他们一条好莱坞法则:"Don't call me; I'll call you."。

值得注意的是,通知机制隐含框架思路。在分层场合,容易编写作为下层模块的导演(Server2)和上层的其他类;在不分层时,事实上导演(Server2)和定义(回调接口)抽象方法的类,与提供实现的类之间,有一条隐藏的层次线。(书中将它们放在一个包中,我发现也挺好。作为习题,让你将它们分成两个包)

3.为什么吐槽 [设计模式]的好莱坞法则

以上面的排序为例。有com.xyz.ArraySortDemo、com.xyz.XxxComparator和java.util.Arrays、java.util.Comparator这4个类,谁是好莱坞法则中的me

(1)com.xyz.ArraySortDemo不合适,一个上层应用程序,显然不是me。当然,如果它说"Don't call me; I'll call you." 绝对正确,因为下层模块Arrays等谁知道你的名字啊。(单向依赖原则)

(2)java.util.Arrays不合适。ArraySortDemo明明地调用你,你敢说Don't call me?

(3)java.util.Comparator不合适。在整个程序中,谁要调用你?就像你看见一个美女,大叫“不要抱抱我”,人家美女想过抱抱你吗?那个程序员会在编写父类如动物的代码时,念叨我要调用“狗”、Dog、Dooog的代码(你知道子类的名字吗?)。另一方面,GoF在模板方法模式中,也知道要强制子类程序员在override时显式地调用父类代码(改进语义的override),所以,父类说"Don't call me; I'll call you." 太荒唐。[如果在介绍动态绑定的实现机制时,你使用好莱坞法则还可以理解。但是在讨论框架或设计模式时提这个,就像军长们讨论解放那个省,你个班长要插嘴,说:“如果攻打一个地堡,是用手榴弹还是啥啥啥”,不是一个级别的谈资]

(4)com.xyz.XxxComparator不合适。上层模块就不合适,再说了,GoF都说父类要调用你!

总之,在这种场合,没有一个类能够作为好莱坞法则中的me。这就是我在涉及代码时,不将框架与好莱坞法则联系起来的原因。在泛泛而谈时,使用IoC好过好莱坞法则,不深究的话,好莱坞法则似乎也说明了框架与库函数的不同。如在C语言中,有库函数

void __cdecl qsort(void *, size_t, size_t, __cmpfunc *);

你编写CmpFunc,然后QSortDemo(void)函数体中调用库函数并将CmpFunc作为实参。你说应用程序将CmpFunc代码块传递给库函数也行,你说库函数执行了CmpFunc代码块也行,你说库函数调用了CmpFunc代码块也行。反正库函数说"Don't call me; I'll call you.",把Don't call me;去掉比较恰当(QSortDemo明明调用了你)。这种场合适用的术语是回调。C函数指针

4.IT有几个好莱坞法则

所以,据不完全统计,有4个:

讨论框架与库函数两者的区别时,控制反转IoC= (借用的)好莱坞法则,如Martin Fowler

很2的说法:如Hollywood Principle,控制反转IoC= 依赖注入 =好莱坞法则。它们把“IoC容器= 依赖注入容器”,经过它们白痴的大脑,利用数学的消除公因式的方法,得到了IoC= DI。
GoF的:这指的是一个父类调用一个子类的操作,而不是相反:好莱坞法则 = 动态绑定的实现。
我采用的好莱坞法则:好莱坞原则 = 通知机制 = 观察者模式

所以,除了2之外,你可以选择任何一种好莱坞法则。

最后说明一下,我的好莱坞法则,完整版应该是"You(你们) don't call me; I'll call you."

上层模块的男女老少演员们(Client1、Client2,复数的you)被框架/下层模块中的导演(IServer)告之了一条好莱坞法则——你们不要轮询我,但是导演调用的是IClient(单数的you)。



 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: