Service Provider Framework Pattern Example

This example is created based on the explanation found in the “Effective Java, Second Edition, Chapter 2: Creating and destroying objects”. This pattern about the framework which provide multiple implementations with the compact API. Complete decoupling of implementations made this pattern interesting to me always Open-mouthed smile.  There are three main components: Service, provider, registration and Service Access API.

In this example, there can be number of traveling implementations, for example, using bus, train, aircraft, car and so on. They are service providers. All these service implementations need be hide behind the registration. User doesn’t worry about the service implementation an only stick to the Service API.

TravelServiceProviderFramework

Let’s see the important TravelService interface which is the client completely rely on. I would like to keep this very simple with one service.

package com.ojitha.travel.service;

public interface TravelService {
	String getVehical();

}

The particular service must be provided by the service provider. That provider is not visible to the user when the user is not register the provider. However, in some cases such as JDBC, user need to have access to the provider to register the provider.

package com.ojitha.travel.provider;

import com.ojitha.travel.service.TravelService;
//provider interface.
public interface Provider {
	TravelService createService();
}

However, in this example, Provider is hide.

package com.ojitha.travel.provider.train;

import com.ojitha.travel.provider.Provider;
import com.ojitha.travel.service.TravelService;

public class TrainProvider implements Provider {

	@Override
	public TravelService createService() {
		// provide train service
		return new TrainService();
	}

}

Other important component is provider registration. Which is a self initiate instance and follow the static factory method to provide singleton. All the service are registered.

package com.ojitha.travel.service.registration;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import com.ojitha.travel.provider.Provider;
import com.ojitha.travel.provider.train.TrainProvider;
import com.ojitha.travel.service.TravelService;

public class TravelServices {
	//make the class non-instantiable
	private TravelServices(){
		providers.put("train", new TrainProvider());
	}
	
	private static final Map<String, Provider> providers = new ConcurrentHashMap<String, Provider>();
	
	//register the provider
	public static void registerProvider(String name, Provider provider){
		providers.put(name, provider);
	}
	
	//service access API
	public static TravelService newInstance(String name){
		Provider provider = providers.get(name);
		if (provider == null){
			throw new IllegalArgumentException("Provider not found");
		}
		
		return provider.createService();
	}
	
}

In this registration class, all the provider implementations are encapsulated. User can refer to the particular implementation using only pre-defined values such as “train”.

package com.ojitha.travel.client;

import com.ojitha.travel.service.TravelService;
import com.ojitha.travel.service.registration.TravelServices;

public class TravelClient {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// the service access API is accessed
		TravelService travelSerivce = TravelServices.newInstance("train");
		//Service API is accessed
		System.out.println("traveled by "+ travelSerivce.getVehical());

	}

}

Typical registered Service implementation as follows  which is not directly access by the above client.

package com.ojitha.travel.provider.train;

import com.ojitha.travel.service.TravelService;

public class TrainService implements TravelService {

	@Override
	public String getVehical() {
		
		return "Train";
	}

}

Comments

Popular posts from this blog

How To: GitHub projects in Spring Tool Suite

Spring 3 Part 7: Spring with Databases

Parse the namespace based XML using Python