First of all, to be clear about this: There is nothing like a best Design Pattern. There is only Design Patterns that match your needs and Design Patterns that don't match. It always depends on the situation. Thus, I will provide a description of each pattern that helps to identifying the useful ones.
Solution 1 - Builder constructing a single instance
The builder pattern is used to build complex products with many different possible configurations. We can further divide this pattern, however you will often see a mix of these:
Expression Builder
This type of Builder is used to provide a nice API to the programmer using the builder. A common example is the implementation of a fluid interface. Essentially, the builder provide an internal DSL to the programmer.
Construction Builder
This type of Builder is used to encapsulate a complex construction algorithm. As opposed to the Expression Builder, it often has a very simple interface.
Solution 2 - Builder constructing multiple instances
This is the most complex and flexible solution. It uses a builder pattern to build a factory. The factory will create instances as configured by the builder. So you have the flexibility of the builder pattern but at the same time you can create multiple instances from a single configuration.
Solution 3 - Factory
If you have exactly one configuration to build your product, just go for a simple factory. The factory alone knows how to build a new instance.
Solution 4 - Abstract Factory
If you have a fixed set of configurations to build the product, you can use the abstract factory pattern by implementing a new concrete factory for each of the configurations. For example:
- use basic auth and a given header
- use no auth and no header
Solution 5 - Factory Methods
This is quite similar to the Factory and Abstract Factory pattern: You have a fixed set of configurations. However, as opposed to these patterns, you do not implement each factory as a class but as a method.
Note, that this is not that different from the Abstract Factory pattern, since Java 8 comes with lambdas and the Supplier<T>
interface. This enables you to pass any factory method as a supplier into other objects, effectively making it an abstract factory.
Summary
In general, the difference between the builder and the factory patterns is, that each factory knows exactly how to build its product. It might require additional parameters to do so. However, a single builder can be used to create products with very different configurations. It is also common practice to encapsulate complex construction algorithms in a builder.
So what should I use?
Without knowing the context where you want to use the Factory/Builder, it is nearly impossible to answer this question. Also, you will nearly never see pure patterns in real code. Patterns are just an idealized form to communicate about abstract design ideas. What I mean by that is that there is often a mix of different patterns.
One example for this is the builder that constructs the factory: You can also let the builder directly construct multiple product instances, however the idea is easier to communicate by using the term "factory".
Also, the Construction Builder and Expression Builder will often be mixed in code, but it is good to think about them as separate concepts.
The same is true for the Builder and Factory patterns. Most of the time you will start with a simple Factory Method. Over the time you add more Factory Methods. Also, the existing Factory Methods will become more complex. At some point you will start to extract the Factory Methods to separate Factory classes. As a Factory becomes more complex, it slowly turns into a Construction Builder.
Example
The class DocumentBuilderFactory
is a good example for how patterns are used in real code: The name implies that it is a Factory that creates a Builder that can build xml documents. Lets look further into this:
DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(...)
The class DocumentBuilderFactory
really contains two factories: The static methods are factory methods creating products of the type DocumentBuilderFactory
. An instance of this class produces products of the type DocumentBuilder
. The product of a DocumentBuilder
is of the type Document
.
DocumentBuilderFactory
Now lets have a look which pattern does and does not match the class DocumentBuilderFactory
and why. First, the class provides two static Factory Methods to create an instance of itself. These methods also kind of implement the Abstract Factory Pattern: You can choose which concrete factory should be created by providing the name and class loader of it.
Now, that we are done with the static factory methods, lets have a look at an instance of the class DocumentBuilderFactory
. The name implies that this is a factory. Indeed, as we saw above, it is an implementation of the abstract factory interface given by the type DocumentBuilderFactory
. However, one can argue that this is too complex to be a factory, since it contains quite a few methods to pass configuration parameters, which are used to create the product. This type of interface would better match the Expression Builder pattern.
Thus, this class is a great example for my statement that you will often find a mix of patterns in real code. After all, this class is just a class. A class that can create instances of several different types by using a set of configuration options. The point of naming it like well known patterns is to give the programmer an idea about what the class is doing. Nothing more and nothing less.
DocumentBuilder
To me, this class can be seen as a Construction Builder: The parse()
method performs a complex operation and the class itself has a quite simple interface. However, it is also a simple factory when using the newDocument()
method: It does not perform a complex operation.
Again, this is an example of a class that mixes the implementation of multiple patterns.