【设计模式】服务定位器模式

前言

服务定位器模式(Service Locator Pattern)是控制反转原理的实现方式之一。本文详细介绍该模式,并提供了 UML 图和示例 Java 代码。

服务定位器模式

服务定位器模式实现了按需返回服务实例。在该模式中,应用所有需要的服务都会被注册到服务定位器中,并通过 ID 唯一标识。应用需要哪个服务,用这个 ID 就能从服务定位器中得到这个服务的实例。服务定位器模式解耦了服务调用者和具体的服务实现。该模式的 UML 类图可以表示如下:

服务定位器模式包含如下组件:

  • Client:服务调用者,向服务定位器发出服务调用请求
  • Service Locator:客户端/服务端间通信入口,从 Cache 中返回服务
  • Cache:缓存、复用服务
  • Initializer:创建、注册服务到 Cache 中
  • Service:服务具体实现

Java 示例

让我们看一个具体的例子来理解服务定位器模式,准备代码如下:

// Java program to 
// illustrate Service Design Service 
// Locator Pattern 
  
import java.util.ArrayList; 
import java.util.List; 

首先,定义服务 Service 接口以及两个具体服务实现:

// Service interface 
// for getting name and 
// Executing it. 
  
interface Service { 
    public String getName(); 
    public void execute(); 
}

// Service one implementing Locator 
class ServiceOne implements Service { 
    public void execute() 
    { 
        System.out.println("Executing ServiceOne"); 
    } 
  
    @Override
    public String getName() 
    { 
        return "ServiceOne"; 
    } 
} 
  
// Service two implementing Locator 
class ServiceTwo implements Service { 
    public void execute() 
    { 
        System.out.println("Executing ServiceTwo"); 
    } 
  
    @Override
    public String getName() 
    { 
        return "ServiceTwo"; 
    } 
} 

接着定义 Service Locator 类,该服务器类实现了一个服务 Cache 以及一个服务实例返回函数 getService。getService 函数会优先返回缓存的服务实例,如果没有,则通过 Initializer 创建。

// Locator class 
class ServiceLocator { 
    private static Cache cache; 
  
    static
    { 
        cache = new Cache(); 
    } 
  
    public static Service getService(String name) 
    { 
        Service service = cache.getService(name); 
  
        if (service != null) { 
            return service; 
        } 
  
        InitialContext context = new InitialContext(); 
        Service ServiceOne = (Service)context.lookup(name); 
        cache.addService(ServiceOne); 
        return ServiceOne; 
    } 
} 

上面提到的 Cache 类和 Initializer 类具体实现如下:

class Cache { 
    private List<Service> services; 
  
    public Cache() 
    { 
        services = new ArrayList<Service>(); 
    } 
  
    public Service getService(String serviceName) 
    { 
        for (Service service : services) { 
            if (service.getName().equalsIgnoreCase(serviceName)) { 
                System.out.println("Returning cached "
                                   + serviceName + " object"); 
                return service; 
            } 
        } 
        return null; 
    } 
  
    public void addService(Service newService) 
    { 
        boolean exists = false; 
        for (Service service : services) { 
            if (service.getName().equalsIgnoreCase(newService.getName())) { 
                exists = true; 
            } 
        } 
        if (!exists) { 
            services.add(newService); 
        } 
    } 
} 

// Checking the context 
// for ServiceOne and ServiceTwo 
class InitialContext { 
    public Object lookup(String name) 
    { 
        if (name.equalsIgnoreCase("ServiceOne")) { 
            System.out.println("Creating a new ServiceOne object"); 
            return new ServiceOne(); 
        } 
        else if (name.equalsIgnoreCase("ServiceTwo")) { 
            System.out.println("Creating a new ServiceTwo object"); 
            return new ServiceTwo(); 
        } 
        return null; 
    } 
}

最后是 Client 类,测试代码效果:

// Client class 
class ServiceConsumer { 
    public static void main(String[] args) 
    { 
        Service service = ServiceLocator.getService("ServiceOne"); 
        service.execute(); 
  
        service = ServiceLocator.getService("ServiceTwo"); 
        service.execute(); 
  
        service = ServiceLocator.getService("ServiceOne"); 
        service.execute(); 
  
        service = ServiceLocator.getService("ServiceTwo"); 
        service.execute(); 
    } 
} 

输出结果:

Creating a new ServiceOne object
Executing ServiceOne
Creating a new ServiceTwo object
Executing ServiceTwo
Returning cached ServiceOne object
Executing ServiceOne
Returning cached ServiceTwo object
Executing ServiceTwo

总结

与依赖注入的区别

两种模式都是控制反转原理的实现,即对象不应该知道如何构造其依赖项。不同点在于,对于服务定位器模式,Client 仍然需要对依赖项的创建负责,虽然借助了 Service Locator 来完成。

优缺点

优点:

  • 应用可以在运行时,选择添加或移除依赖服务
  • 应用和服务间松耦合,唯一的连接仅仅在于服务定位器的服务注册

缺点:

  • 服务定位器的服务注册使代码更加复杂,不得不维护比较多的服务唯一标识 ID
  • 如果依赖有错误,只能在运行时报错,不能在编译时发现
  • 由于应用仍要负责创建依赖项,因此不能 mock 依赖,不方便写测试代码

参考资料

[1] 30分钟学会UML类图
[2] Baeldung: Service Locator Pattern
[3] GeeksforGeeks: Service Locator Pattern

posted @ 2020-03-21 12:07  黄挤挤  阅读(1082)  评论(0编辑  收藏  举报