1

I'm surprised this doesn't seem to have been asked before, or at least I can't find it anywhere. I know this is more of a philosophical question, but is there any particular reason not to use a factory (I'm assuming everyone knows the benefits of the factory pattern, so I won't go into that) for instantiating public API classes? In other words, is there some reason to use a public constructor for such classes other than the fact that it's quicker to code which is maybe better for really simple, small APIs?

  • The point of a factory is to decouple API users from concrete implementation class types. So this question is essentially the same as asking "Should every parameter type actually be an interface rather than a class?" There's definitely no consensus about that, and there'll be no consensus about this question either. – Kilian Foth Jun 26 '15 at 12:10
4

Simplicity.

Factory (not abstract factory, just an ordinary one in its simplest form) pattern is useful when initializing a class requires complex business logic. Putting this logic in the constructor is often out of question, and having the logic in a static method within the class leads to class pollution (and sometimes poor discoverability).

In a case where the initialization logic is very straightforward, you don't need to create additional classes and replicate this logic. Doing that will lead to more LOC, less maintainable code and more work overall, with the only benefit of uniformization (that is every object of the code base is necessarily created through a corresponding factory).

Examples:

  1. A Product class constructor accepts a ProductId id argument and loads the additional information from the database.

    This is a perfect candidate for factory pattern. Database access shouldn't happen in a constructor, the constructor is expected to be very simple, fast and are often not expected to throw exceptions.

  2. A Price class constructor takes int amount and Money unit arguments.

    This is a perfect example where you won't have a factory. Why would you? The only thing it will do is to pass the arguments to the constructor. More complexity, no benefits.

  3. A Rebate class constructor takes int amount and AmountUnit unit arguments. Depending on the amount and the unit, it can express a percentage of the total price, an absolute rebate or a “this product is free” (which, for the business, needs to be considered differently from 100% rebate).

    Here, you may start writing code within the constructor, and if the code grows, migrate it to a factory. At the first sight, it is unclear whether the complexity of the business rules justifies a creation of a dedicated factory class. Over the lifespan of Rebate class source code, the team may switch several times between the initialization within the constructor and the initialization through a factory class.

0

One example worth looking at involves Java collections. The type hierarchy beneath Collection<T> has quite a few subinterfaces and concrete types. Each data structure has advantages and disadvantages. Sometimes they have additional properties above and beyond the basics of a particular data structure such as thread-safety or maintaining insertion order in an otherwise jumbled structure (e.g. some maps and sets).

To help with this, there are several factories built-in to the JFC. The Collections class can wrap an existing collection in one that is unmodifiable (the collection itself is immutable, the elements it contains may or may not be). It can wrap a collection in a synchronized wrapper, offering basic thread-safety (might not work as well as the specialized collections in the concurrency package). It can wrap a single element in a singleton collection (not the singleton pattern, just a collection with one element).

Those are all factory methods: one calls a method that returns an object rather than calling a constructor directly. They are useful because it allows one to adapt a collection that might be created out of one's control (e.g. in a framework or library) with additional constraints (e.g. mutability or concurrency) without explicitly creating a new object.

Guava takes this a step further with its Collection Utilities. It contains many more factory methods for creating new collections of various types with various properties. Construction can be more concise and expressive this way. At a conceptual level there is nothing more to explain beyond the JFC Collections class, but I will note that Google went through quite a bit of effort to build these factories because they add value.


This is just one example of where factories can be useful, but should they always be used?

There are two main reasons for using a factory:

  • Construction is complex. Constructors should be simple: do they need additional data or processing? For example, loading data from a file or database should not be done in a constructor, but may be needed to create an object. It makes more sense to put that extra processing in a factory method.

  • The implementation needs to vary independent of where objects are created. Might you want to use a different implementation at a future time? Might it be variable through a plugin architecture? Might you want to inject a mock for testing?

Many API classes fail these tests. For example, a string will always have the same implementation and does not require much extra processing. The core API of most languages/runtimes should be fairly simple for two reasons:

  • It is solving simple problems, e.g. "represent a sequence of characters in a string."

  • It needs to be applicable to a wide range of problems and highly reusable, which is at odds with a highly specialized class.

This simplicity is at odds with the requirements for using a factory. When a type is simple enough that a no-arg or one-arg constructor is all that is ever needed, and the implementation will always be the same, there is no point to using a factory.

Note that I am talking about the core API here (strings, collections, streams), not stuff like databases, GUI, or other more complex libraries.

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