2017-08-29

Generic Service Locator in Java for Game Development

ServiceLocator is a very useful decoupling pattern to avoid too many Singletons. It's structure is always the same, so I thought it should be possible to create a generic one, to decrease code duplication. Well, here is what I've come up with,  guess someone with more knowledge about generics could create something better, but it is better than nothing.

PS: I thought it must be possible to have a static method generating NulServices / Stub Services in the Classes implementing IService, but I couldn't find out how to demand a static method createNullService in an interface. Tell me if you have an idea.

Here ist the abstract ServiceLocator


import java.util.HashMap;
import java.util.Map;

/**
 * IServiceLocator
 *
 * @author Georg Eckert 2017
 */

public abstract class ServiceLocator
{
    private final Map<Class<? extends IService>, IService> services, nullServices;


    public ServiceLocator()
    {
        services = new HashMap<>();
        nullServices = new HashMap<>();
        provideNullServices();
    }

    public abstract void provideNullServices();

    public <T extends IService> T get(Class<T> serviceInterface)
    {
        if(!services.containsKey(serviceInterface) && !nullServices.containsKey(serviceInterface))
        {
            throw new IllegalArgumentException("No such Service in this Module.");
        }

        if(services.containsKey(serviceInterface))
            return (T) services.get(serviceInterface);
        else {
            System.err.println("SERVICES: No " + serviceInterface.getSimpleName() + " service injected, returning NullService.");
            return (T) nullServices.get(serviceInterface);
        }
    }

    public <T extends IService> void provide(T service , Class<? extends IService> serviceInterface)
    {
        services.put(serviceInterface, service);
    }

    public <T extends IService> void provideNull(T service , Class<? extends IService> serviceInterface)
    {
        nullServices.put(serviceInterface, service);
    }
}

and a concrete one, overriding the abstract provideNullServices method


import de.limbusdev.utils.ServiceLocator;

/**
 * GuardiansServiceLocator
 *
 * @author Georg Eckert 2017
 */

public class ConcreteServiceLocator extends ServiceLocator
{
    private ServiceLocator instance;

    public ServiceLocator getInstance()
    {
        if(instance == null) instance = new ConcreteServiceLocator();
        return instance;
    }

    @Override
    public void provideNullServices()
    {
        provideNull(new AbstractFactory()
            {
                @Override
                public AbstractProduct createProduct(int ID)
                {
                    return null;
                }
            },
            AbstractFactory.class
        );
    }
}

all real services must be provided somewhere in your initialization code

// Providing a concrete Service
ConcreteServiceLocator.provide(new ConcreteFactory());

// Retrieving the concrete service without knowing its exact implementation
AbstractFactory factory = ConcreteServiceLocator.getService(AbstractFactory.class);

No comments:

Post a Comment