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.