Java – How to decouple simple factory and default implementation

dependency-injectiondesign-patternsjavaservice-locator

I have a simple factory class (FileResources) with static factory methods providing a default implementation (DefaultFileResource).

public final class FileResources {
    private FileResources() {}
    public static FileResource get(Path path) {
        return new DefaultFileResource(path);
    }
}

The problem with this class is that it is tightly coupled with the default implementation (DefaultFileResource).

Given a new requirement that classes part of the API (FileResources) cannot directly depend on classes not part of the API (DefaultFileResource) I figured that the only solution is inversion of control, either dependency injection or a service provider service locator.

Is it possible to decouple FileResources from the default implementation (DefaultFileResource)?

As far as I know this is only doable by injecting the dependency into FileResources through a constructor, field or setter method.
The main problem is that the constructor of FileResources is private, furthermore it only has static factory methods.

The alternative is the factory method pattern. There would be an abstract factory (an API class), and there would be concrete factories (non-API classes) that could be even injected to decouple the API and non-API classes.

Either way a dependency container is necessary to handle the wiring.

What do you think? Am I overthinking this?

Best Answer

No, an IOC container is not necessary to decouple the API and non-API classes. Because they're not really coupled.

FileResources is coupled to Path and to FileResource, but not to DefaultFileResource. That is, if you change Path or FileResource, there is a risk of breaking FileResources. Albeit not a large one. Those are pretty thin layers; that's an acceptable coupling.

However, if you change DefaultFileResource, as long as it still conforms to FileResource, you cannot break FileResources.

You wouldn't even break any tests for FileResorces. They test only that a DefaultFileResource is returned, under the default conditions, and that's fine.

Even more importantly, your calling code is not coupled to any particular implementation of FileResource either. That's good. In fact it's the whole point of a Factory Method. (And this is a Factory Method. There is no Factory Class distinction, in the context you've used it; it doesn't matter whether the Factory Method exists in another class.)

However, your calling code IS coupled to FileResources. Most notably, if you change the implementation of FileResources, there is every chance you would break any unit tests that are testing your calling code, because the get method cannot be stubbed out.

That is a much bigger concern than any knowledge FileResources has about DefaultFileResource.

The only reason you would use an IOC container for the factory is if you want to build up DefaultFileResource with its dependencies. I would avoid that unless you need it and, if you should need it, I would take a look around at the Service Locator pattern and why is should be avoided, before I decided on the use of that IOC container.