Why does this ShapeFactory use conditional statements to determine what object to instantiate. Dont we have to modify ShapeFactory if we want to add other classes in the future? Why doesnt this violate the open closed principle?
The conventional object-oriented wisdom is to avoid
if statements and replace them with dynamic dispatch of overridden methods in subclasses of an abstract class. So far, so good.
But the point of the factory pattern is to relieve you from having to know about the individual subclasses and work only with the abstract superclass. The idea is that the factory knows better than you which specific class to instantiate, and you will be better off working only with the methods publishd by the super class. This is often true and a valuable pattern.
Therefore, there is no way that writing a factory class can forego the
if statements. It would shift the burden of choosing a specific class to the caller, which is exactly what the pattern is supposed to avoid. Not all principles are absolute (in fact, no principle is absolute), and if you use this pattern you'd assume that the benefit from it is greater than the benefit of not using an
The example probably uses a conditional statement because it is the simplest. A more complex implementation might use a map or configuration or (if you want to be really fancy) some kind of registry where the classes can register themselves. However there is nothing wrong with using a conditional if the number of classes is small and changes infrequently.
Extending the conditional to add support for a new subclass in the future would indeed strictly speaking be a violation of the open/closed principle. The "correct" solution would be to create a new factory with the same interface. That said, adherence to the O/C principle should always be weighed against other design principles like KISS and YAGNI.
That said, the code displayed is clearly example code which is designed to show the concept of a factory and nothing else. E.g. it is really bad style to return null as the example does, but more elaborate error handling would just obscure the point. Example code is not production quality code, any you shouldn't expect it to be.
The pattern itself doesn't violate the Open/Closed Principle (OCP). However, we violate the OCP when we use the pattern incorrectly.
The simple answer to this question is as follows:
- Create your base functionality using Factory Method Pattern.
- EXTEND your functionality by using the Abstract Factory Pattern
In the provided example, your base functionality supports three shapes: Circle, Rectangle, and Square. Suppose you need to support Triangle, Pentagon, and Hexagon in the future. To do this WITHOUT violating OCP, you must create an additional factory to support your new shapes (let's called
AdvancedShapeFactory) and then use AbstractFactory to decide what factory you need to create in order to create whatever shapes you need.
If you're talking about the Abstract Factory pattern, the decision making often isn't in the Factory itself but in application code. It's that code that chooses what concrete factory to instantiate and pass around to the client code that will use objects produced by the Factory. See the end of the Java example here : https://en.wikipedia.org/wiki/Abstract_factory_pattern
Decision making not necessarily implies
if statements. It could read the concrete Factory type from a config file, derive it from a map structure, etc.
If you think about the Open-Close at class level with this factory you are making other class in your system Open-Close, for example if you have other class that take one Shape and calculate the area (typical example) this class is OpenClose because it can calculate the area for new types of shapes without modification. Then you have another class that draws the shape, another class that take N shapes and return the bigger one and you can think in general that the other classes in your system that deals with shapes are Open-Close (at least about shapes). Looking at the design the factory enables the rest of the system to be Open-Close and off course the factory itself ITS NOT Open-Close.
Off course you can make this factory open-close too, via some kind of dynamic loading and your entire system can be Open-Close (you can add new shapes dropping some jar in the classpath for example). You need to evaluate is this extra complexity is worth depending on the system your are building, not all systems need pluggable features and not all the system need to be completely Open-Close.
The open-closed principle, as the Liskov substitution principle, applies to class trees, to inheritance hierarchies. In your example, the factory class is not in the family tree of the classes it instantiates so it cannot violate these rules. There would be a violation if your GetShape (or more aptly named, CreateShape) were implemented in the Shape base class.
It all depends how you implement it. You can use
std::map to hold function pointers to functions creating objects. Then the open/close principle is not violated. Or switch/case.
Anyway, if you do not like the factory pattern, you can always use dependency injection.