5

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?

  • 1
    Why does FileResources need to be final, and does get need to be static? If you make it a plain old java object, it fits in dependency injection very well. But if you're using dependency injection (like Spring) why do you need the FileResources class at all? Can you just use the Spring bean factory to create your ...FileResource? – Rob Mar 11 '14 at 17:45
  • It does not need to be final with the private constructor already there. The method is static because it is a static factory method, mainly for convenience. FileResources contains complex instantiation logic. The java.nio.file.Paths class uses the same approach. I cannot use Spring. – Vorzard Mar 11 '14 at 19:17
  • As an alternative, you might think about the builder pattern. That's the flip side of Factory. Instantiate builder, provide some inputs, then .build(). I actually prefer that pattern over Factory, because it seems a little more flexible IMHO. – Rob Mar 11 '14 at 19:37
  • I often use a Builder to construct immutable objects, mainly for consistent state for objects with optional fields, but with only one argument a static factory method is more suitable. The actual FileResources class contains get, create, and locate methods, each with complex logic to get, create or locate a non-executable data file, and return a FileResource pointing to it. – Vorzard Mar 11 '14 at 19:59
6

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.

  • Thanks, the issue is much more clear now. It seems that the knowledge of FileResources is a bigger concern, than it's knowledge of a particular FileResource implementation. The similar java.nio.file.Paths class uses the Service Locator pattern, I am already familiar with it's drawbacks though. The whole issue with the knowledge of DefaultFileResource came up because I divided an existing codebase into an API and an IMPL source set, and set up the IMPL classes to depend on the API classes, but not the other way around. – Vorzard Mar 11 '14 at 19:07
  • This is a great answer describing differences between acceptable and unacceptable coupling. Would you care to elaborate on how to decouple the API from the implementation in this case, since that seems to be what lead to the question? – Mike Partridge Mar 12 '14 at 14:13
1

What to do really depends on how far you want to go into abstractions. A simple way to take is to add a static .getInstance(Path path) method to the abstract FileResource class (if it's an interface, you would change it into an abstract class), which would then return a default instance. This may not exactly meet your needs (FileResource would now be dependent on the default implementation instead), and your app may run into inheritance issues if you change an interface into an abstract class.

The second option is to create a FileResourceFactory. It can either be a class or an interface with implementing classes. A single implementing class is simpler, but creates dependencies on implementations, while an interface means more complexity but potentially less coupling.

Since this is a static class, dependency injection through the constructor is not possible, but you can create a setter method for a static FileResourceFactory, and inject it when you need to. Two warnings arise here, though:

  • You cannot guarantee that the static factory has been set before your get method is called
  • You are wandering dangerously close to global variable territory with a static member on a class like this

I also want to point out that, at some point, someone eventually needs to grab something concrete. With the dependency injection approach, you can push this pretty far out, but eventually someone will be dependent on getting a concrete instance somewhere.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.