0

I have the following package and class structure in my project,

package de.mycompany.jakarta.order;
import de.mycompany.ordermanagement.order.OrderCancellationService;
public class DrugOrderManager {
private static final DrugOrderManager INSTANCE = new DrugOrderManager();

private DrugOrderManager() {
}

public static DrugOrderManager getInstance() {
    return INSTANCE;
}

public void cancelOrder() {
    OrderCancellationService.getInstance().process();
}   }



package de.mycompany.ordermanagement.order;
public class OrderCancellationService {
private static OrderCancellationService INSTANCE = new OrderCancellationService();

private OrderCancellationService() {
}

public static OrderCancellationService getInstance() {
    return INSTANCE;
}

public void process() {
    
}   }

My intention is that the OrderCancellationService should be only called by the DrugOrderManager and none of any other class/service should call it directly. I am trying to make DrugOrderManager as a gateway to all the services. How do I restrict this visibility? Please advise

Hari
  • 441
  • 6
  • 15

3 Answers3

1

To do what you want, you can simply create OrderCancellationService as private static class inside DrugOrderManager.

In your case though, I imagine you will have more similiar classes to OrderCancellationService - so putting all those classes in a single package and making only DrugOrderManager public, while making all other services package-private, may be a better approach. You expose only single entrypoint for the consumer, it's manageable and easy to understand the code.

If you can't move the classes then I guess there is no solution in Java.

Shadov
  • 5,421
  • 2
  • 19
  • 38
1

There several obvious and bad ways to do that:

  • make OrderCancellationService a private classe inside DrugOrderManager
  • put OrderCancellationService & DrugOrderManager in same package

Another solution is to use another class, that will get access to all the instances, and will forward them to the proper implementation. Only that class would get access to everything. The services themselve would only get access to what that common class provide them. This is far from perfect, there still a "god" object, but this is limited to one object and there no more limitation on packages.

Here is an example where Factory is the object that has all instances and 2 services, ServiceA and ServiceB. ServiceB has access to ServiceA, but ServiceA doesn't have access to ServiceB.

In file Factory.java:

class Factory {
  private static Map<Class, Object> INSTANCES = new HashMap<>();
  private ServiceA serviceA;
  private ServiceB serviceB;

  public static void register(Class clazz, Object instance) {
    instances.put(clazz, instance);
  }



static {
  Class.forName("ServiceA");
  Class.forName("ServiceB");


  serviceA = (ServiceA)(INSTANCES.get(ServiceA.class));
  serviceB = (ServiceB)(INSTANCES.get(ServiceB.class));
  
  serviceB.setServiceA(serviceA);
}

}

}

In File ServiceA.java:

class ServiceA {
  private ServiceA INSTANCE = new ServiceA();

  private ServiceA() {}

  static {
    Factory.register(ServiceA.class, INSTANCE);
  }
}

In File ServiceB.java:

class ServiceB {
  
  private ServiceB INSTANCE = new ServiceB();
  private ServiceA serviceA;

  private ServiceB() {}
  
 
  public setServiceA(ServiceA serviceA) {
    this.serviceA = serviceA;
  }
  static {
    Factory.register(ServiceB.class, INSTANCE);
  }
}

Other implementations are also likely possible with reflexion API.

Just make the implementation private and let the interface be public.

To me what you are trying to achieve is not worth the effort.

What is quite easy to do is to at least make ALL implementation fully private and only have the public entry point of the service available through interfaces thanks to Java 9 modules. As any service is typically only a few public method in its facade, this provide 95% of the feature without nasty tricks. Sure service can see each ither public interface, but they have no way to even access the private implementation.

Before Java 9 to make an implementation really private, you can use maven, make interface & implementation modules for all services, and the implementation module be a runtime dependency to the interface so any ref to impletation would fail at compile time but the implem would be present at runtime.

Service Locator Pattern, Dependency Injection & Spring

You can see my example as very crude implementation of what we often call a Service Locator or the concept of dependency injection.

Spring (https://spring.io/) is the de facto standard for that in the Java world. You should check a few tutos/training on the net to see what it can do.

Nicolas Bousquet
  • 3,990
  • 1
  • 16
  • 18
0

You could try to define OrderCancellationService as a private class inside DrugOrderManager, maybe this will work as your expectation....

Mark Score
  • 52
  • 2
  • 6