控制反转有助于提高程序的可测试性

控制反转,英文是Inversion of Control,简称IoC。这个概念在JAVA的Spring框架中比较常见,在.NET的开发中,Ioc或者其的变种依赖注入DI(Dependency Injection)是不太常见的。一般来说,类A要使用类B,类A会在其内部对类B进行控制,比较典型的做法就是在类A中创建一个类B的实例;而控制反转,就是把一个已经创建好的类B实例交给类A去控制。
例子:

有一个Member的类,他需要调用一个Web service的客户端接口,在这个类的构造函数里面,创建了一个WebServiceClientA对象。这里的WebServiceClientA类是实现了WebServiceClient接口的。

class Member
{
    WebServiceClient client;
    Member()
    {
        client = new WebServiceClientA("10.60.0.11");
    }
}

问题来了,对于这个类,我们怎么去测试?在构造函数里面已经初始化了一个WebServiceClientA,并且指定了这个WebServiceClientA的IP是10.60.0.11。那假如说开发环境不能连接到这台服务器上,那么怎么测试。这个问题可以通过对IP地址这个参数写到配置文件里面就能解决,其实这个跟IoC没有什么关系。

class Member
{
    WebServiceClient client;
    Member()
    {
        client = new WebServiceClientA(ConfigurationManager.AppSettings["ServerIP"]);
    }
}

现在又有问题了,如果WebServiceClientA类没有完成,但是此时却要进行测试,怎么办?又假如说WebServiceClientA类是很难被创建的,怎么办?Mock也有劲使不上啊。
其实在构造函数里面是不应该有创建对象的操作,也就是尽可能不要有new操作,我们把WebServiceClient作为构造函数的一个参数,传递进去,这样程序的可测试性就有了提高,这时候可以对WebServiceClient使用Mock对象了。

class Member
{
    WebServiceClient client;
    Member(WebServiceClient c)
    {
        client = c;
    }
}

这跟著名的好莱坞原则(Hollywood Principle)有几分相似,don’t call us, we’ll call you。你不要自己创建一个实现了WebServiceClient接口的对象,我给你传一个。

Leave a Reply

Your email address will not be published. Required fields are marked *