Reputation: 11
I have a programming problem that I want to know if it can be solved using Java design techniques. I have class Service and I have a class Client. A client requests a service and if it's not already existing, then it will be created (i.e. new service object). If the service has been created (i.e by a different client or even the same client), then the Service class will not create a new object. Instead, the client can be added to the service (if not already added). Other fields and methods of the Service class will be applied to clients of the same service.
public class Service {
private String service;
private ArrayList<Integer> clients;
//.... other field
public Service (String s){
this.service = s;
clients = new ArrayList<>;
}
public void addClient(int c){
clients.add(c);
}
//..other methods
}
public class Client {
private int clientID;
private ArrayList<String> services;
public Client(int id){
clientID = id;
services = new ArrayList<>;
}
public void addService(String s){
services.add(s);
}
public void requestService() {
for(int i=0; i<services.size();i++)
Service s = new Service(services.get(i));
}
}
The problem with the above approach is that new service objects with the same service would be created by different clients.
I'm currently reading up on on static factory. As far as my research goes:
public class Service(){
private Service(){
}
public static Service createService(String service){
if (/*service doesn't exist*/)
return new Serivce();
else
return null;
}
//....
}
This above code would prevent creating a new object instance. However, if service already exists and therefore returns null, then a new client cannot join (or use) that particular service.
Upvotes: 1
Views: 3122
Reputation: 1298
I think what you're looking for is the typical lazy singleton pattern. Have a look here with focus on the Initialization-on-demand holder idiom which is the best approach in my opinion as it's 100% thread safe.
public class Service {
// Private constructor. Prevents instantiation from other classes.
private Service() {
}
// Initializes Service singleton.
private static class SingletonHolder {
private static final Service INSTANCE = new Service();
}
// gets the one and only instance of Service
public static Service getInstance() {
return SingletonHolder.INSTANCE;
}
}
EDIT:
Looking at you static factory code, it looks like you're actually looking for a Service container that provides one and only service instances for a particular key (service name, or service class for better type safety) you pass. Below you'll find a very basic not thread-safe implementation using classes as key. There are a lot other better thread-safe implementations if you google around for "thread-safe multitons" or similar. However if you're now on a green field, I deeply recommend to make use of a dependency injection framework, which already does the whole job for you apart from bringing many other benefits. I'm a fan of Google's Guice in particular but the decision what to use heavily depends on your project. If you need help for the decision I suggest posting a new thread with the details of your project and what you need this service container for.
public class ServiceContainer {
private final Map<Class<?>, Service> map = new HashMap<>();
// Private constructor. Prevents instantiation from other classes.
private ServiceContainer() {
}
// ServiceContainer singleton.
private static class SingletonHolder {
private static final ServiceContainer INSTANCE = new ServiceContainer();
}
// gets the one and only instance for a particular Service class
@SuppressWarnings("unchecked")
public static <T extends Service> T getInstance(final Class<T> serviceClass) {
// init service if not initialized yet
if (!SingletonHolder.INSTANCE.map.containsKey(serviceClass)) {
SingletonHolder.INSTANCE.map.put(serviceClass, createService(serviceClass));
}
return (T)SingletonHolder.INSTANCE.map.get(serviceClass);
}
private static <T extends Service> T createService(final Class<T> serviceClass) {
try {
return serviceClass.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
// please handle this exception properly
throw new RuntimeException(e);
}
}
}
Upvotes: 0