The reason you don't see static factory methods listed in the GoF book is because this pattern doesn't use polymorphism in any interesting way. Your diagram suggests this, but most languages do not support the structure it shows. Specifically, a static method cannot also be virtual. There is no instance object to dispatch on. It is not possible to override a static method in the way you could override an instance method (some languages have class methods though, where we can dispatch on the class object). While the GoF book is only concerned about showcasing object-oriented patterns, static factory methods are still a common pattern in many languages.
What the page you linked to describes are two distinct patterns.
First we have the common pattern that rather than exposing a constructor of our class, we provide a static method. Many languages allow us to define multiple constructors and resolves them through method overloading, but static methods can have different names which is far more programmer-friendly. The linked page uses Color
objects as an example. new Color(float, float, float)
might be obvious, but can't be overloaded with different color models (RGB vs. HSV). Static methods can disambiguate through the name: Color::make_rgb(float, float, float)
versus Color::make_hsv(float, float, float)
. This is about good API design, but has nothing to do with object orientation.
Using (static) factory methods also offers us more freedom in the implementation. We could map the HSV colors to RGB internally. Or Color
could be abstract and used different subclasses for each color space, without exposing this difference to the user. This implementation detail is entirely encapsulated:
public static Color makeRGB(float r, float g, float b) {
return new ColorRGB(r, g, b);
}
public static Color makeHSV(float h, float s, float v) {
return new ColorHSV(h, s, v);
}
In my experience, such methods make code less ambiguous and make an API more approachable and maintainable. In C++, there's the additional benefit that static methods can use template argument deduction, whereas class template parameters have to be provided explicitly in a constructor call.
Factory methods have another interesting feature: We can decide in our code which concrete type to instantiate. I have used this in some parsers:
Expression makeBinaryExpression(Expression left, String operator, Expression right) {
if (operator == "+") return new Addition(left, right);
if (operator == "-") return new Subtraction(left, right);
if (operator == "*") return new Multiplication(left, right);
if (operator == "/") return new Division(left, right);
throw new ParseException("Expected +, -, *, /, but got " + operator);
}
You are right that such static methods pose an extensibility problem. How could I add the CMYK color model to the Color
class? I can't, without editing the Color
class, or replacing it with a different but compatible class (by changing imports, or by C++ templates). If extensibility is required, the API should be made virtual – we'll come back to that in a minute.
However, not all code needs to be polymorphic. Especially if the behaviour is “pure” and doesn't interact with any outside state and when the behaviour is nor likely to change, non-virtual methods still have a place.
Also, the open-closed principle is only relevant when you can't change the source and release a new version. This is the case with published APIs where the publisher and consumer are in different organizations. In such a case, there must be a clear API and this interface must be carefully designed to allow consumers to adapt it to their needs – often by requiring dependency injection and consistently expressing the API in terms of user-implementable interfaces. But if the code is only used within the same project, the OCP is not as relevant (relying on interfaces can still make it easier to refactor, though).
Now let's get back to polymorphism. The GoF book does list the Factory Method Pattern and likens it to a “virtual constructor”. The whole point here is that the factory method is virtual and overrideable. This of course means that a factory method on the Product
class is useless, since we would need to have an existing Product
in order to create it. Instead, there is a different Creator
class. The factory method is then an example of the Template Method Pattern which can be overridden by Creator-subclasses to provide a specific Product-subclass. The Abstract Factory Pattern is an example of a creator with multiple related factory methods.
Occasionally, factory methods are also used as a dependency injection mechanism. The base class defines some template methods to construct the dependencies, which can then be changed through subclassing. However, my experience shows that using small Strategy objects to open up dependency construction is a more flexible and simpler approach rather than requiring the whole target class to be inherited.