板砖拍Martin Fowler

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/yqj2065/article/details/81585196

今天yqj2065用板砖,拍Martin Fowler的IoC容器和依赖注入模式》(Inversion of Control Containers and the Dependency Injection pattern[1] )。将我在其他博客中关于他这篇文章的吐槽,条理化。

1.Martin Fowler介绍

Martin Fowler(马丁·福勒),国际著名的OO专家,敏捷开发方法的创始人之一,现为ThoughtWorks公司的首席科学家.

在面向对象分析设计、UML、模式、软件开发方法学、XP、重构等方面,都是世界顶级的专家.他著有几本经典书籍:《分析模式》、《UML精粹》和《重构》等.

他是一个低调的人,我个人很喜欢他。但是,该拍的时候,还是要拍一下。

2.IoC容器和依赖注入模式》的主要错误

在查看他的中文版文章时,有个编者语。【编者语:最近研究IoC,在网上搜索到很多网页推荐阅读Martin Fowler的一篇名叫Inversion of Control Containers and the Dependency Injection pattern的文章。点击到该文章页面便吓了一跳:这什么文章啊,简单一个网页PageRank居然是7!要知道,国内几大门户网站也都还没有到这个值呢!也难怪,Martin Fowler被誉为软件开发教父,他的文章,肯定有N多人拜读。细细读来,感觉大师确实很厉害,文章条理清晰,论证深入,结论明确!】吹!

其主要错误:

1)不懂什么是IoC!

2)没有说清楚依赖注入模式服务定位器(Service Locator)模式

现在按照他的文章,逐字逐句地批注。

《·一个简单的例子》

简单明了地说:MovieLister(按照我的一贯做法,用Client)依赖于接口MovieFinder(我一般用IServer),要解决的问题就是MovieLister要如何初始化MovieFinder变量(即创建MovieFinder的特定对象),也即 针对接口编程/抽象依赖原则的使能问题

Martin Fowler的描述,则比较晦涩。『我们希望MovieLister类能够与MovieFinder的任何实现类配合工作,并且允许在运行期插入具体的实现类,插入动作完全脱离我(原作者)的控制。这里的问题就是:如何设计这个连接过程,使MovieLister类在不知道实现类细节的前提下与其实例协同工作。……所以,现在的核心问题就是:如何将这些插件组合成一个应用程序?这正是新生的轻量级容器所面临的主要问题,而 它们解决这个问题的手段无一例外地是控制反转(Inversion of Control)模式。』

Martin Fowler通过该例子将话题引入到下一节

《·控制反转》

『几位轻量级容器的作者曾骄傲地对我说:这些容器非常有用,因为它们实现了控制反转。这样的说辞让我深感迷惑:控制反转是框架所共有的特征,如果仅仅因为使用了控制反转就认为这些轻量级容器与众不同,就好象在说我的轿车是与众不同的,因为它有四个轮子。(1)

(2)问题的关键在于:它们反转了哪方面的控制?我第一次接触到的控制反转针对的是(3)用户界面的主控权。早期的用户界面是完全由应用程序来控制的,你预先设计一系列命令,例如输入姓名、输入地址等,应用程序逐条输出提示信息,并取回用户的响应。而在图形用户界面环境下,UI框架将负责执行一个主循环,你的应用程序只需为屏幕的各个区域提供事件处理函数即可。在这里,程序的主控权发生了反转:从应用程序移到了框架。对于(4)这些新生的容器,它们反转的是如何定位插件的具体实现。在前面那个简单的例子中,MovieLister类负责定位MovieFinder的具体实现——它直接实例化后者的一个子类。这样一来,MovieFinder也就不成其为一个插件了,因为它并不是在运行期插入应用程序中的。而这些轻量级容器则使用了更为灵活的办法,只要插件遵循一定的规则,一个独立的组装模块就能够将插件的具体实现注射到应用程序中。因此,我想我们需要给这个模式起一个更能说明其特点的名字——“控制反转”这个名字太泛了,常常让人有些迷惑。与多位IoC 爱好者讨论之后,我们决定将这个模式叫做“依赖注入”(Dependency Injection)。』

(1)控制反转是框架所共有的特征。没错,但是轻量级容器并不是框架,因此与IoC一点关系都没有。Martin Fowler嘲笑几位轻量级容器的作者,"我的轿车是与众不同的,因为它有四个轮子"。然而,提出DI的Martin Fowler,其实五十步笑一百步。他也错了,Spring DI容器根本不是轿车,它只有两个轮子。Spring和God一样,一个工具箱!!!

(2)在网络文章中有大量的对控制反转加以说文解字的文章。说文解字的根源在于Martin Fowler的一句话,“问题的关键在于:它们反转了哪方面的控制?”。例如中文学院的文科生写的东西的来源。

什么是控制反转(Inversion of Control)中,我强调,IoC是一个毫无意义的垃圾术语!IoC中,所谓控制,指设计决定的控制权,设计决定的控制权,设计决定的控制权。解读IoC,不要在“控制”上异想天开,说些不知所云的话。

★控制反转/Ioc,反映了某些应用程序员的失落心情:从自由自在变为受控于人。它描述了一种现象:软件设计决定的控制权,由应用程序员转移到框架设计者手中。

如果你一开始就设计某个框架,显然,你不会感觉到任何IoC!

(3)用户界面的主控权?

在这里,Martin Fowler属于胡说八道。我说过:如果框架设计者为接口命名为soooooort(),应用程序员编写回调函数时,要注意数清楚o的个数。为什么要你数清楚o的个数?这不是搞笑的,而是要你知道:你(应用程序员)受制于人!

用户界面的主控权?你可以写一个Helloworld级别的程序,如:

package tips;
import static tips.Print.*;//替代System.out.println()等
import java.util.Scanner;
/**
 * @author yqj2065
 * @version 0.1
 */
public class InputDemo{
    public static void demo() {
        Scanner scanner = new Scanner(System.in);
        p("请输入姓名: ");
        pfln("Hello! %s!", scanner.next());
        p("请输入年龄: ");
        pfln("OK! %d!", (int)scanner.nextDouble());
        p("是男生吗?(true or false): ");
        pfln("ye! %s!", scanner.nextBoolean()?"男":"女");
    }
}

并且大声地、傲娇地宣布:我编写了一个控制正转的程序(这是讽刺Martin Fowler)。图形用户界面环境,UI框架之所以被称为框架,是因为“UI框架将负责执行一个主循环,你的应用程序只需为屏幕的各个区域提供事件处理函数即可”——Martin Fowler回答的不错。但是,结论【程序的主控权发生了反转:从应用程序移到了框架】错了,你感觉到的“反转”,仅仅是因为UI框架是可以运行的。这就是为什么我要你们编写不能够自运行的框架SortTest->IntSort的原因。

你想不想把框架SortTest->IntSort修改成可以运行的?简单得很,参考【实验1:框架设计者】,在框架中添加一个runFramework函数即可。

(4)这些新生的容器,它们反转的是如何定位插件的具体实现。

IoC不是变形金刚,在不同场合就反转不同东西。我抽烟自己点火,如果别人帮我点火——它们反转的是什么

对于简单地单层依赖,如Client→IServer,有代码:

package chap3.init;
import commons.*;
import java.io.IOException;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.beans.factory.BeanFactory;
public class Client {
    public static void test2() throws IOException {
        BeanFactory bf = new FileSystemXmlApplicationContext("spring1.xml");
        IServer h = (IServer)bf.getBean("IIServer");
        h.doSth();
        //比较   
        h = (IServer)yqj2065.util.God.create("IIServer");
        h.doSth();
    }    
}

请注意,为什么God比Spring牛逼?God一个小小的工具类,却反映了依赖注入的本质——使用配置文件(后面再谈)!

从God和Spring,没有什么被反转

3.依赖的建立

插入一点我写的东西。【1.5 依赖的建立】

遵循针对接口编程/依赖抽象原则时,Client如何初始化IServer的对象呢?

以Client为边界(可以想象成国界),Client所依赖的IServer对象,要么进口,要么国产

1. 进口

所谓进口,描述了Client将所依赖的IServer对象的创建推给外界的情况。包括如下情景。

  • IServer作为方法的形参。这是最单纯地依靠外界创建IServer对象的方式。在《第二章行为参数化》中,讨论IServer的特殊情况——函数接口。
  • IServer作为Client的成员变量时,可以通过构造器或setter方法,要求外界创建IServer对象并传入。这种情况,表明Client有很多方法需要用到IServer,而又不希望将IServer作为每个方法的参数。提请注意,本情况与《3.2依赖注入》貌合神离
  • Client有一个类似List<IServer>或IServer数组的成员变量,Client需要提供注册方法。外界调用注册方法将创建的IServer对象加入到List<IServer>或IServer数组中。

2. 国产

所谓国产,描述了Client将所依赖的IServer对象的创建,由Client自己创建Client本身调用其他类创建。包括如下情景。

  • Client用new表达式创建IServer的子类型对象;此时违反了依赖抽象原则。
  • Client用反射创建字符串指定的IServer的子类型对象;
  • Client使用工具类创建IServer的子类型对象。这将是《第三章创建对象》要讨论的主要内容。当工具类使用配置文件时,称之为依赖注入;当工具类不使用配置文件时,称之为工厂模式

Martin Fowler的文章之所以有误,因为他没有全面分析 Client如何初始化IServer的对象。他如同盲人摸象一样,拿着Spring等“轻量级容器”,就开始哔哔哔哔地说事。

Client使用一个工具类来创建所依赖的对象,有什么值得大惊小怪之处吗?不嫌麻烦,Client可以每次用反射机制,来创建字符串指定的IServer的子类型对象。

Martin Fowler的主要错误2)没有说清楚依赖注入模式服务定位器(Service Locator)模式,一言以蔽之:当工具类使用配置文件时,称之为依赖注入;当工具类不使用配置文件时,称之为工厂模式

 


[1] http://www.martinfowler.com/articles/injection.html

猜你喜欢

转载自blog.csdn.net/yqj2065/article/details/81585196