API designers might know the struggle: you have a package defining the public API as interfaces and then you have another package containing the implementation. How do you make the implementation accessible to the outside but also restrict unwanted instantiations?
Forbidding unwanted usage of your API actually makes it easier to use because it is harder to be used the wrong way.
Let's look at this simple example and start with a public service interface:
package com.yourdomain.api;
public interface PublicService {
// .. Service methods
}
Then, within the internal package, the service's implementation:
package com.yourdomain.internal;
import com.yourdomain.api.PublicService;
public class PublicServiceImpl implements PublicService {
// ... Service method implementations
}
You do not want client code to directly instantiate the PublicServiceImpl
class,
because it should not depend on the actual implementation. One way to do so is putting a
factory class with static methods into the api
package like:
package com.yourdomain.api;
import com.yourdomain.internal.PublicServiceImpl;
public class PublicServiceFactory {
public static PublicService create() {
return new PublicServiceImpl();
}
}
Now, using the factory, client code does not need to know about the existence of the
PublicServiceImpl
. The problem is, this approach does not enforce the usage of the
factory. Clients could still go and call the implementation's constructor because it
needs to be public in order for the factory to see it.
My proposal for solving this issue, is to use an instance of the factory class as guard
for instantiating the PublicServiceImpl
. To do so, we introduce a constructor argument
within our implementation:
package com.yourdomain.internal;
import com.yourdomain.api.PublicService;
import com.yourdomain.api.PublicServiceFactory;
public class PublicServiceImpl implements PublicService {
// an instance of the Factory must be passed in order to construct an instance of the
// service
public PublicServiceImpl(PublicServiceFactory guard) {
if (guard == null) {
throw new IllegalArgumentException("no guard supplied");
}
}
// ... Service method implementations
}
Now, to instantiate the service, you first need an instance of its factory. The next step is to prevent the factory class from being instantiated from the outside and to pass an instance of itself when constructing the service instance.
package com.yourdomain.api;
import com.yourdomain.internal.PublicServiceImpl;
// make class final to prevent inheritance
public final class PublicServiceFactory {
private PublicServiceFactory() {
// hide constructor from others
}
public static PublicService create() {
// b/c of the private constructor we are the only one who can create the guard
// instance
final PublicServiceFactory guard = new PublicServiceFactory();
return new PublicServiceImpl(guard);
}
}
This way we enforce that the factory is the only one place where our service can get instantiated and clients must commit themself to a special malicious intent in order to create an instance of the implementation themself.
Note that this approach might not be suitable in every case. One disadvantage is that you introduce a package cycle between the internal and the api package. This prevents you from separating them into different artifacts.
In guice-async-extension I'm using
this technique to pass a guice Module
from the internal- to the api package. This also
allows to make all the service implementations package private
because they will be
instantiated by guice and thus the only public class within the internal package is the
module for configuring the guice bindings.