Service Locator Pattern

The Service Locator Pattern is a design pattern used to provide dependencies (services) to a client without exposing the creation logic. Instead of the client directly instantiating or managing services, it queries a Service Locator, which manages the lifecycle and lookup of services.

It is often used to decouple service consumers from concrete implementations. However, note that in modern applications, dependency injection (DI) is generally preferred over service locator, since DI makes dependencies explicit.


Steps of Service Locator Pattern

  1. Service Interface – Defines the contract.
  2. Concrete Services – Implement the service interface.
  3. Initial Context / Service Registry – Responsible for creating or locating services.
  4. Cache – Stores services for future lookups.
  5. Service Locator – Provides services to clients.
  6. Client – Uses the service via the locator.

Example in Java

// Step 1: Define a service interface
public interface Service {
    String getName();
    void execute();
}

// Step 2: Create concrete services
public class ServiceA implements Service {
    @Override
    public String getName() {
        return "ServiceA";
    }

    @Override
    public void execute() {
        System.out.println("Executing ServiceA...");
    }
}

public class ServiceB implements Service {
    @Override
    public String getName() {
        return "ServiceB";
    }

    @Override
    public void execute() {
        System.out.println("Executing ServiceB...");
    }
}

// Step 3: Initial Context – creates/looks up services
class InitialContext {
    public Service lookup(String serviceName) {
        if (serviceName.equalsIgnoreCase("ServiceA")) {
            System.out.println("Looking up and creating a new ServiceA object");
            return new ServiceA();
        } else if (serviceName.equalsIgnoreCase("ServiceB")) {
            System.out.println("Looking up and creating a new ServiceB object");
            return new ServiceB();
        }
        return null;
    }
}

// Step 4: Cache for services
import java.util.ArrayList;
import java.util.List;

class Cache {
    private List<Service> services = new ArrayList<>();

    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 = services.stream()
                .anyMatch(s -> s.getName().equalsIgnoreCase(newService.getName()));
        if (!exists) {
            services.add(newService);
        }
    }
}

// Step 5: Service Locator
class ServiceLocator {
    private static Cache cache = new Cache();

    public static Service getService(String serviceName) {
        // First check cache
        Service service = cache.getService(serviceName);

        if (service != null) {
            return service;
        }

        // If not found, lookup from InitialContext
        InitialContext context = new InitialContext();
        Service newService = context.lookup(serviceName);
        cache.addService(newService);
        return newService;
    }
}

// Step 6: Client
public class ServiceLocatorPatternDemo {
    public static void main(String[] args) {
        Service service1 = ServiceLocator.getService("ServiceA");
        service1.execute();

        Service service2 = ServiceLocator.getService("ServiceB");
        service2.execute();

        // This time it should be fetched from cache
        Service service3 = ServiceLocator.getService("ServiceA");
        service3.execute();

        Service service4 = ServiceLocator.getService("ServiceB");
        service4.execute();
    }
}

Output

Looking up and creating a new ServiceA object
Executing ServiceA...
Looking up and creating a new ServiceB object
Executing ServiceB...
Returning cached ServiceA object
Executing ServiceA...
Returning cached ServiceB object
Executing ServiceB...

✅ Key Points:

  • The client code only interacts with the ServiceLocator, not with concrete service implementations.
  • Services are cached after the first lookup to improve performance.
  • This pattern is useful when working with legacy code or frameworks where DI is not easily applicable.
  • In modern applications, Dependency Injection (Spring, Guice, CDI, etc.) is generally preferred over Service Locator.

Leave a Reply