Namespaces

Variants
Actions

Please note that as of October 24, 2014, the Nokia Developer Wiki will no longer be accepting user contributions, including new entries, edits and comments, as we begin transitioning to our new home, in the Windows Phone Development Wiki. We plan to move over the majority of the existing entries over the next few weeks. Thanks for all your past and future contributions.

Decoupling components using a generic service locator

From Wiki
Jump to: navigation, search
Article Metadata
Article
Created: User:BjoernQ (30 Dec 2008)
Last edited: hamishwillee (16 Aug 2013)

It's a good idea to decouple classes and not to hard wire the dependencies between implementation classes.

The rise of the dependency injection frameworks is because it makes it so easy to use. Unfortunately all the Java™ Platfrom, Standard Edition (Java SE) frameworks rely heavily on reflection which isn't available on Java ME

A good alternative to dependency injection is to use a service locator. It's quite easy to build a very simple but powerful generic service locator for Java Platfrom, Micro Edition (Java ME) with only two classes.

Code:

package de.mobilej.appcontext;
 
import java.util.Hashtable;
 
/**
* A simple yet powerful generic service locator.
* @author bjoern
*/

public class ApplicationContext {
 
private Hashtable registeredTypeToImplementation = new Hashtable();
private Hashtable registeredTypeToIsSingleton = new Hashtable();
private Hashtable registeredTypeToSingletonInstance = new Hashtable();
 
private static ApplicationContext defaultInstance = new ApplicationContext();
 
private ApplicationContext(){
}
 
 
/**
* Creates a NEW context.
* @return a new instance of ApplicationContext
*/

public static ApplicationContext createContext() {
return new ApplicationContext();
}
 
 
/**
* Returns the default application context.
* There is exactly ONE default application context.
* @return the default application context
*/

public static ApplicationContext getDefault(){
return defaultInstance;
}
 
/**
* Get an instance of the requested type if such a type is registered.
* Attention: Currently the requested type have to match exactly.
* That means if you have registered something for type A. And A is
* a subtype of B you won't get anything when requesting B.
*
* @param requestedType
* @return an instance of the requested type
*/

public synchronized Object getComponent(Class requestedType ){
Object instance = null;
if(registeredTypeToIsSingleton.get(requestedType)!=null){
instance = registeredTypeToSingletonInstance.get(requestedType);
if(instance==null){
instance = createInstance(requestedType);
registeredTypeToSingletonInstance.put(requestedType,instance);
}
} else {
instance = createInstance(requestedType);
}
return instance;
}
 
 
public synchronized void register(Class registeredType, Class clazzImpl){
registeredTypeToImplementation.put(registeredType,clazzImpl);
}
 
public synchronized void registerSingleton(Class registeredType, Class clazzImpl){
registeredTypeToImplementation.put(registeredType,clazzImpl);
registeredTypeToIsSingleton.put(registeredType,Boolean.TRUE);
}
 
private Object createInstance(Class requestedType) throws RuntimeException {
Class implClazz = (Class) registeredTypeToImplementation.get(requestedType);
 
if(implClazz==null){
throw new RuntimeException("No implementation available for " + requestedType+".");
}
 
Object instance = null;
try {
instance = implClazz.newInstance();
} catch (InstantiationException ex) {
throw new RuntimeException("No implementation available for " + requestedType+". "+ex.toString());
} catch (IllegalAccessException ex) {
throw new RuntimeException("No implementation available for " + requestedType+". "+ex.toString());
}
 
// if possible let the component wire up it's dependencies
if(instance instanceof Initializable){
((Initializable)(instance)).setup(this);
}
 
return instance;
}
 
 
 
}
 
 
 
 
package de.mobilej.appcontext;
 
/**
* An interface to be implemented by classes that whant to wire it's dependencies at creation time.
* @author bjoern
*/

public interface Initializable {
/**
* Here you have to request and set your dependencies.
* @param ctx
*/

public void setup(ApplicationContext ctx);
}


How to use these two classes?

First of all you need an instance of ApplicationContext. Often the only instance you need is the singleton instance created for you by calling:

ApplicationContext.getDefault()

But you can certainly create new instances by using:

ApplicationContext.createContext()

This is very useful for unit testing.


On the context you can register components.

ctx.register(MyInterface.class, MyImplementation.class);

So whenever an instance of "MyInterface" is requested a new instace of MyImplementation will be created.

Additionally you can register the component as a singleton:

ctx.registerSingleton(MyInterface.class, MyImplementation.class);

Here everytime you request an instance of "MyInterface" you get the same instance.

Please note that no instance is created until it's first use.

To request an instance you use:

MyInterface comp = (MyInterface) ctx.getComponent(MyInterface);

Here you have no dependency (and no knowlege) of the used implementation. If you want to switch the implementation you only have to change one single line where the components get registered. That's it.

Especially this makes it easy to write unit tests and let the class under test lookup only mocks instead of real implementations.

When an component get instanciated there is one special trick you can do:

When the implementation implements the interface "Initializable" then it's "setup" method is called with the instanciating ApplicationContext as a parameter. So you can easily request all the dependencies the implementation has from the context.

Example:

        public void setup(ApplicationContext ctx) {
dependency = (TestInterface1) ctx.getComponent(TestInterface1.class);
}

Here we get an implementation for the TestInterface1 from the context. If the component was initialized from a test context used in a unit test we might get a mock object.

When running in the real application we will get a real implementation.

It is also possible to cache the context and lookup components later on.

So with only some lines of code we get a powerful generic service locator which helps us decoupling implementations and increase testability.

This page was last modified on 16 August 2013, at 01:26.
45 page views in the last 30 days.

Was this page helpful?

Your feedback about this content is important. Let us know what you think.

 

Thank you!

We appreciate your feedback.

×