public interface RequestProcessorFactoryFactory

September 20, 2008 – 1:10 am

beans曾经在 reddit 上看到这个 Apache 的 FactoryFactory ,觉得很好笑,只是想,大概这样的名字就是严格按照某些设计模式做出来的吧。不过笑过之后也并没有去细想。

最近自己开始用起 Java 来,因为以前学过这个语言,所以很容易就上手了,之后差不多很多东西都很自然,直到有一天我发现自己不小心做了一个叫做 FeatureCollectionFactory 的接口出来,才觉得似乎是该好好想一想了。

有一些东西,自己以前也时常听到或者看到,但是觉得太“企业级”了或者太“Java”了,并没有去关注。比如 Factory 这个东西,为什么会需要 Factory 呢?我觉得,主要就是因为 new 语句太“死板”了,比如:

Fruit fruit = new Fruit();

这里后面可以写 new Apple()new Pear() 或者是 new Grape() 什么的,但是无论是写任意一个,都必须要作为一个语句写死了,在编译的时候就完全确定了,但是有时候希望具体创建什么对象要等到运行时才能确定。于是就有了 Factory 。

除了这个,Factory 还可以做其他用处,比如,控制对象的创建等。然而,为何在 Ruby 或者 Python 这样的动态语言里面没有看到满天飞的 Factory 呢?原因也很简单:Factory 是为了在 Java 这样的相对静态的语言里将一些东西推迟到运行期,实现更加动态一点的特性,而这些在动态语言里是非常自然的。例如下面的 Ruby 代码:

fruit = fruit_class.new()

其中我可以在运行时将变量 fruit_class 赋值为 Apple 或者 Pear 或者(得益于 Duck Typing)其他任何合适的东西。“类”对象本身就可以当作一个 Factory 来使用了。在 Java 中虽然也有反射和 Class 对象,但是那些东西用起来实在是太笨重了,于是便有了一堆一堆的 Factory 。

再来说说最近在 Java 世界里了解到的一个叫做 Dependency Injection 的东西,很神秘的一个名字,一开始我还以为是如 InvokeDynamic 之类的东西呢,查阅了一番之后才发现其实也是一个简单的概念。

我的理解大致就是这样:原本我们写程序都希望能尽量自动化,比如,我要 new 一个 Computer 出来,Computer 会自动去查找电源,并在需要的时候 new 一个电源,电源可能又会初始化大楼的总闸、发电厂……如果有幸中间某个步骤是已经初始化好的,直接用就可以了,否则大概要追溯到宇宙大爆炸。这是一种按需取用的方式,一切都在后台自动搞定。

然而 Dependency Injection 则把这些东西反过来,比如,电源不能让 Computer 自己去查找,而是要你先把电源初始化好之后再手工传递给 Computer ,当然,在这之前你可以需要先手工打开大楼的总闸,或者之前要先建立一个发电厂……或者你得从宇宙大爆炸开始做起。

看起来好像根本就是在自找麻烦啊,对比这两种方法,我就想起了小学时解应用题的两种方法:一种是设定一个未知数,把它当作已知的东西,列出一个等式来,问题自然就解决了;另一种是直接倒推,得出一个算式就可以算出来了。后者需要很高的技巧,而且很容易算错,看起来这里的 Dependency Injection 就是在走这样的一条路,为何要把运行得好好的高度自动化的装配线拆散了手工组装呢?

问题就出在“运行得好好的”上面。很多时候程序实际上并不能那么完美地运行,一般通过事前的 Unit Testing 和事后的 Debugging 。被 Debugging 折磨了这么多年的程序员们如今都渐渐开始喜欢 Unit Testing 了,然而整个流程太自动化了,牵一发而动全身:我只是要 new 一个 Computer 而已,却有可能会触发宇宙大爆炸,而且一切都在后台进行,根本没有插手的余地,完全没有可测试性。

有了 Dependency Injection ,就变得容易多了,诸如 Computer 或者“发电厂”之类的 Java 对象是几乎完全独立的存在,需要手工把他们装配在一起。但是手工装配太痛苦了,便通过 XML 配置文件来完成(于是 Java 世界里又遍地都是 XML 和“配置文件”了),在不同的场合把不同的对象组装在一起(比如,在单元测试的时候,可以使用一个 Mocking 的发电厂)便成了一个 Java 程序。这样让我颇有些不习惯:配置文件和源代码之间的关系变得非常紧密了,因为程序的很大一部分逻辑现在跑到了配置文件里面去,仅仅靠源代码根本无法构成一个完整的程序了。

看起来 Dependency Injection 在动态语言里也是可以推行的,只是相对于成堆的 XML 来说,大概直接用脚本或者加上一些 DSL 的元素更受欢迎一些。 :)

  1. 4 Responses to “public interface RequestProcessorFactoryFactory”

  2. 我认识 DI 是从 Spring 开始的,不知道你是不是。我对 Spring 的认识停留在 2.5 初期。我比较想不通的一点是为什么 Spring 后期的版本开始采用 annotation 的方式完成 DI 。使用 XML 虽然有些笨重,但是完全将 bean 的实现逻辑与 bean 之间的依赖关系解耦了,使得应用在不需要修改 Java 源码的情况下通过修改 XML 配置就可以重组应用。然而采用 annotation 的方式,虽然语法简化了些,却把 bean 之间的依赖关系又回归到了源码中,这样不是丧失了 DI 很大一部分的灵活性么……始终没想通。

    By rhythm on Sep 20, 2008

  3. @rhythm,
    我也是从 Spring 那里了解到的,不过也许我看的文档比较旧吧,还不知道可以用 annotation 的方式。还有它说的那些什么 auto wiring 之类的,我也觉得很悬 啊,太不靠谱了…… ^_^

    By pluskid on Sep 20, 2008

  4. 但是为什么ror之类的里面不需要DI呢

    By Sparkle on Sep 22, 2008

  5. @Sparkle,
    RoR 里面感觉很多东西都是全自动的,就是那种所谓的 Convention over Configuration ,但是单元测试之类的也可以做得很好,我觉得也许是因为 RoR 本身是一个非常特定领域的框架,它的各种行为都是预先定义好了的,所以在这个方面来说比较好做,而且易用性在这里比灵活性更重要一些了。

    By pluskid on Sep 24, 2008

Post a Comment