6.3.2 Service Locator(服务定位器)

    服务定位器封装了服务/对象/组件查找,为它们提供一个全局的入口。例如在EJB中,为了查找所需要的组件/服务,我们使用javax.naming.InitialContext对象去查找。

    figure_0087_0061

    为了直观说明,我们这里为上述例子实现了一个非常简单的服务定位器。首先我们构建一个ServiceLocator类,代码如下所示。

    figure_0087_0062

    figure_0088_0063

    从上述实现可以看到,我们创建了一个HashMap<String, Object>对象来持有这些服务对象,Service1和Service2的对象其实是按照单例创建的。这样我们的Client类的实现如下所示。

    figure_0088_0064

    使用之前,我们需要使用ServiceLocator.configure()初始化,以下是我们的测试代码。

    figure_0089_0065

    服务定位器封装了查找逻辑,隐藏了组件/服务/对象之间的依赖关系,客户对象只要依赖于它就能获取想要的组件/服务/对象。

    工厂方法模式和服务定位器的区别是:服务定位器为整个应用组件/服务/对象的获取提供了单一的入口,而一个工厂只提供特定类型的实例化,如果一个工厂能提供所有组件/服务/对象的装配和实例化,那它就被进化为服务定位器。

    使用服务定位器时,容器/框架侵入了代码,降低了代码移植性,单元测试也相对比较麻烦。以上述场景为例,为了测试Client的方法,我们需要替换掉Service1服务对象,代码如下所示。

    figure_0089_0066

    figure_0090_0067

    我们在正式测试之前,用MockService1服务对象替换掉原来的Service1服务对象,即ServiceLocator.registerService("service1",mockService1)。在测试结束之后,我们要把原来的服务对象注册回去,否则很可能影响其他测试。于是,在最后,为了避免测试过程中发生异常中断,我们把重新注册回原来service1服务对象的逻辑放在finally代码块里。由于每个测试都要做类似的清理操作,给单元测试带来了极大的不便。