设计模式のService Locator Pattern

文章目录

Service Locator Pattern

geeksforgeeks的原文 ,我理解性的翻译了下,添加了些自己的理解。

用 抽象层 封装 获取服务 的过程。

使用一个被称为 service locator的中心注册表,service locator 根据请求返回执行具体任务所需要的的信息。

当服务消费者请求服务实例时,serviceLocator负责返回对应的服务实例。


service Locator

service locator 将 查找服务的API、服务提供者依赖、查找的复杂性以及业务对象的创建做了抽象,仅对服务消费者暴露一个简单的接口,这样将复杂封装在自己内部,使得客户端变的简单。


InitialContext

初始化上下文,类似于 spring里面的容器概念。如果对容器的概念也不清楚,那就只可意会不可言传了。

是整个服务查找和创建过程的起点。将所有的服务提供商放进这个容器里面,如果,后续有新的服务提供商加入进来,也需要将它们放进这个InitialContext容器来。

InitialContext 根据具体的业务对象类型返回具体服务对象。


ServiceFactory

服务工厂对象负责管理 业务服务对象 的生命周期,何时实例化,何时销毁等。


BusinessService

业务服务对象,是一个角色,由客户端需求的服务对象来扮演。所以说,就是具体的服务提供商对象。

业务服务对象 由 服务工厂创建、查找、删除


图例

设计模式のService Locator Pattern

以前的类关系

服务消费者和服务提供商,绑定在一起,增加或删除服务提供商,都需要更改服务消费者中的代码。

在编译的时候,具体的服务提供商的类就需要存在。


设计模式のService Locator Pattern

Service Locator Pattern 模式的类关系

服务提供商和服务消费者之间的耦合,转移到 locator 里面

在编译的时候,具体的服务提供商的类不需要存在。增加或修改服务提供商,服务消费者也不需要改代码。


java code demo

服务接口

/**
* 首先将 服务 的功能,抽取出来。
* 这个抽取很有意义,在后面这个接口将代替具体的服务提供商,参与到逻辑中。
* 将具体的服务提供商,排除在外,这样整体逻辑,将和具体的服务提供商无关。
* 也就是和具体的服务提供商没有耦合关系
*/
interface Service {
	public String getName();
	public void execute();
}

服务提供商类

// 具体的服务提供商 one
class ServiceOne implements Service {
	public void execute()
	{
		System.out.println("Executing ServiceOne");
	}

	@Override
	public String getName()
	{
		return "ServiceOne";
	}
}

// 具体的服务提供商 two 
class ServiceTwo implements Service {
	public void execute()
	{
		System.out.println("Executing ServiceTwo");
	}

	@Override
	public String getName()
	{
		return "ServiceTwo";
	}
}

容器类

/** 
* 上下文对象
* 是一个容器,负责注册和查找具体的服务
* 后续如有新的服务提供商,需要在这里修改代码
* 这里是用硬编码方式,注册服务商。
* 其实容器,还有其他优雅的方式处理,像 spring 那样
*/
class InitialContext {
    
    // 这里面的代码,简单化了
    // 业务复杂的话,这里面应该抽象出一个 ServiceFactory 类,复杂具体服务的创建
	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;
	}
}

缓存类

搭配上面的 InitialContext 更像容器。

/**
* 缓存
*/
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);
		}
	}
}

ServiceLocator 类

// 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;
		}
		// 越看越像 spring里面的 ApplicationContext 
		InitialContext context = new InitialContext();
        // getBean 方法
		Service ServiceOne = (Service)context.lookup(name);
		cache.addService(ServiceOne);
		return ServiceOne;
	}
}

测试类

// Driver class
class ServiceLocatorPatternDemo {
	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();
	}
}


优点

  1. `ServiceLocator` 配合 `cache`,完成了`service`的单例模式。并不需要具体的`service`自己实现单例模式,通过`cache` 完成了单例的需求。这在某些场景下,很实用,假如考虑,创建具体的服务提供商对象很占用资源,但是具体的服务又是第三方提供的,这时候就可以用这种思路实现单例,缓存起来。
  2. 程序可以在运行时动态调整依赖
  3. 解耦合,服务消费者和服务提供者之间并没有之间的联系,所有的联系都在注册表中

缺点

  1. 如果服务提供商不存在,本来应该在编译期间就会被发现的,因为是直接依赖。现在转移到运行期间才能发现。(其实算不得缺点,将依赖耦合解耦了,就这样)

总结

最后,设计模式不能完全理解,或者想不到对应的案列,是正常的。毕竟设计模式是总结会来的经验性准则,需要一定的阅历或者说经历过类似的坑,才能感同身受。

就像后来的我们学 spring 框架,永远也不会多么深刻的理spring的轻量级,轻量在哪里。但是如果你是从EJB 时代过来的,你就会 会心一笑。

上一篇:web.dev tooling.report


下一篇:元素定位方法没错,但运行就是报错,如何解决