3
\$\begingroup\$

For a bigger project of mine, I needed a simple and extensible solution for easy access to replaceable Parts.

This example will show the usage:

  1. Define an Interface, describing the replaceable part Eg: IReportSender
  2. Define the concrete EMailReportSender and TelegramReportSender
    Both will be implementing the IReportSender
  3. On Startup (or anywhere else in the Application) add it to the ServiceLocator:

    ServiceLocator.getServiceLocator().addService(IReportSender.class, new EMailReportSender());
    
  4. Anywhere else in the application get the Service and use it:

    IReportSender reportSender = ServiceLocator.getServiceLocator().getService(IReportSender.class);
    reportSender.SendReport([...]);
    

And now the Code:

import java.util.HashMap;

public class ServiceLocator {

    private static ServiceLocator instance;
    private HashMap<Class<?>, Object> services;

    /**
     * Creates a new instance of the {@link ServiceLocator}
     */
    private ServiceLocator() {
        services = new HashMap<Class<?>, Object>();
    }

    /**
     * Gets the {@link ServiceLocator}
     * @return the current {@link ServiceLocator}
     */
    public static ServiceLocator getServiceLocator() {
        if (instance == null) {
            instance = new ServiceLocator();
        }

        return instance;
    }

    /**
     * Adds a service
     * @param clazz class of the defining interface
     * @param service instance of the actual implementation
     * @return <code>true</code>, if the service was added, otherwise <code>false</code>
     */
    public boolean addService(Class<?> clazz, Object service) {
        if (services.containsKey(clazz) || !clazz.isAssignableFrom(service.getClass())) {
            return false;
        }

        services.put(clazz, service);

        return true;
    }

    /**
     * Gets a service
     * @param clazz class of the defining interface
     * @param <T> defining interface
     * @return instance of the actual implementation
     */
    public <T> T getService(Class<T> clazz) {
        if (!services.containsKey(clazz)) {
            return null;
        }

        Object service = services.get(clazz);
        return clazz.cast(service);
    }

    /**
     * Removes a service
     * @param clazz class of the defining interface
     * @return <code>true</code>, if the service was removed, otherwise <code>false</code>
     */
    public boolean removeService(Class<?> clazz) {
        if (!services.containsKey(clazz)) {
            return false;
        }

        services.remove(clazz);

        return true;
    }

    /**
     * Removes all services
     */
    public void removeAllServices() {
        services.clear();
    }

}
\$\endgroup\$
1
  • \$\begingroup\$ That's a good approach, but I recommend to use spring cdi or something similar. It's more convenient, it decouples object life cycle and dependency management from within your application. \$\endgroup\$
    – slowy
    Jul 5, 2017 at 8:53

1 Answer 1

1
\$\begingroup\$

You have some clean looking code there, but there's a huge problem with Service Locators. They're hidden dependencies. If I reference your class from elsewhere, I'm going to be really confused about the exceptions I'm going to get when I try to use it.

Where is this null EmailService exception coming from?! WTF???

It's much better to use proper constructor injection of dependencies. That makes it crystal clear what is needed to make your class work.

\$\endgroup\$
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.