Design pattern の Service Locator Pattern

Keywords: Java Design Pattern

Service Locator Pattern

geeksforgeeks , I translated it understandably and added some of my own understanding.

Encapsulate the process of obtaining services with an abstraction layer.

Using a central registry called service locator, the service locator returns the information required to perform specific tasks upon request.

When a service consumer requests a service instance, serviceLocator is responsible for returning the corresponding service instance.

service Locator

The service locator abstracts the API of finding services, service provider dependencies, the complexity of finding and the creation of business objects, and only exposes a simple interface to the service consumer. In this way, the complexity is encapsulated in itself, making the client simple.

InitialContext

The initialization context is similar to the container concept in spring. If the concept of container is not clear, it will only be meaningful and unspeakable.

It is the starting point of the whole service discovery and creation process. Put all service providers into this container. If new service providers are added later, they also need to be put into this InitialContext container.

InitialContext returns the specific service object according to the specific business object type.

ServiceFactory

The service factory object is responsible for managing the life cycle, instantiation and destruction of business service objects.

BusinessService

Business service object is a role played by the service object required by the client. Therefore, it is the specific service provider object.

Business service objects are created, found and deleted by the service factory

legend

Previous class relationships

Service consumers and service providers, bound together, adding or deleting service providers, need to change the code in the service consumers.

When compiling, the specific service provider class needs to exist.

Class relationship of Service Locator Pattern pattern

The coupling between service providers and service consumers is transferred to locator

When compiling, the specific service provider class does not need to exist. Add or modify service providers, and service consumers do not need to change the code.

java code demo

Service interface

/**
* First, extract the functions of the service.
* This extraction is very meaningful. Later, this interface will replace the specific service provider and participate in the logic.
* Excluding specific service providers, the overall logic will have nothing to do with specific service providers.
* That is, there is no coupling relationship with a specific service provider
*/
interface Service {
	public String getName();
	public void execute();
}

Service provider class

// Specific service providers
class ServiceOne implements Service {
	public void execute()
	{
		System.out.println("Executing ServiceOne");
	}

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

// Specific service provider two 
class ServiceTwo implements Service {
	public void execute()
	{
		System.out.println("Executing ServiceTwo");
	}

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

Container class

/** 
* Context object
* Is a container responsible for registering and finding specific services
* If there is a new service provider in the future, you need to modify the code here
* Here is a hard coded way to register a service provider.
* In fact, containers are handled in other elegant ways, like spring
*/
class InitialContext {
    
    // The code here is simplified
    // If the business is complex, a ServiceFactory class should be abstracted to create complex and concrete services
	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;
	}
}

Cache class

With the InitialContext above, it is more like a container.

/**
* cache
*/
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 class

// 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;
		}
		// The more it looks, the more it looks like the ApplicationContext in spring 
		InitialContext context = new InitialContext();
        // getBean method
		Service ServiceOne = (Service)context.lookup(name);
		cache.addService(ServiceOne);
		return ServiceOne;
	}
}

Test class

// 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();
	}
}

advantage

  1. `ServiceLocator cooperates with 'cache' to complete the singleton mode of 'service'. There is no need for a specific 'service' to implement the singleton mode. The singleton requirements are completed through 'cache'. This is very practical in some scenarios. If you consider that creating a specific service provider object takes up resources, but the specific service is provided by a third party, you can use this idea to implement a single instance and cache it.
  2. Programs can dynamically adjust dependencies at run time
  3. Decoupling, there is no connection between service consumers and service providers, and all the connections are in the registry

shortcoming

  1. If the service provider does not exist, it should have been found during compilation because it is a direct dependency. You can't discover it until you move to runtime now. (actually, it's not a disadvantage. The dependency coupling is decoupled, that's all)

summary

Finally, it is normal that the design pattern can not be fully understood or can not think of the corresponding case list. After all, design pattern is an empirical criterion to summarize the meeting. It needs some experience or similar pit to feel it.

Just as we learned the spring framework later, we will never deeply understand the lightness and lightness of spring. But if you are from the EJB era, you will smile.

Posted by omelin on Sun, 31 Oct 2021 05:25:15 -0700