The factory method is a design pattern that allows you to define a base class for creating objects, but lets subclasses specify which class to instantiate. Essentially, the factory method enables a class to delegate object creation to its subclasses.
Let's say we are managing the appearance of a game character; for familiarity, let the game be Super Mario. We'll be only managing the different versions of Mario, e.g., Fire Mario, Cape Mario, Invincible Mario, etc. Our primary goal is to instantiate Mario's character.
First, let us consider that we have only one type of Mario's character, i.e., Mario. So, our approach will be to make a class Mario and instantiate it directly wherever needed. In the following code, we have considered instantiation in the StartGame() method of GameManager class only but, it is possible that we have to instantiate Mario in many parts of the code.
Note: The GameManager class is the core part of the code where all smaller segments come together. For instance, we've added the ChangeTheme() method, which helps change themes, and the Operations() method for deciding operations on various game characters. (These methods are introduced to realize the role of GameManager class, i.e., it is dealing with a lot of other things too).
The takeaway from this approach is: Move the responsibility of creating the object (here object of Mario class) to a separate class, say Factory. In other words, We create a Factory class whose only responsibility is to create an instance of Mario class. Now, our code will be:
Here, we replaced direct instantiation with instantiation via a factory. This factory class is popularly known as Simple Factory. Now, product instantiation is done by the factory, and the StartGame() method is just a means of accessing the instantiated product.
Now, consider the original problem statement, which required two types of Mario characters, namely, Fire and Cape Mario.
For this, we will declare an abstract class (MARIO) of Mario's character, and its concrete subclasses will implement different versions of Mario like Fire Mario (FireMario) and Cape Mario (CapeMario). These subclasses are concrete products, and the base class is an abstract product.
Again, we can make a StartGame() method in the GameManager class to instantiate any one type of Mario based upon some parameter (system-specific). The GameManager class will look like
We've seen that introducing a Factory in such code helps to some extent. So, one might think of making different factories for each type of Mario and then creating the corresponding type of Mario. But if we need to perform specific operations on the Mario character, which has been created in the factory, then we have to write the same code in each factory. E.g.
Takeaway: So, we'd like to have a framework that relates operations and the creation of Mario's character together yet still maintains flexibility!
One key idea is to relate all the factories under some interface (or base class). That interface will have two responsibilities: creating Mario's character and essential operations on that character. Let's call this interface (or abstract class) as MarioMaker, which will have the methods CreateMario() and Operations() for necessary operations.
But the question is how CreateMario() decides which type of Mario to instantiate? No worries, it won't decide, at least in the interface itself. What we'll do is we'll subclass MarioMaker into FireMaker and CapeMaker and override the CreateMario() to instantiate FireMario and CapeMario, respectively.
Here's the catch: We are delegating the responsibility of creation of Mario to the subclasses. And then we’ll use the Operations() on the created Mario.
There are still quite a few aspects of this approach. The Operations() method will use an object of type MARIO (which is abstract), due to which this method does not know about the type of Mario it uses; hence it is decoupled from the concrete subclasses of MARIO class.
The final approach that we just discussed is the factory method, where CreateMario() is used to manufacture an object.
Creator (MarioMaker)
Declares the factory method, which returns an object of Product class and operations to be done on the product returned by the factory method.
Note: The Creator needs not to be a purely abstract class or interface every time! We can give some default implementation to the factory method in the Creator class itself that returns a default Concrete Product that the subclasses can further override.
Also, the Creator class is not only responsible for creating products, but it has some operations to do on the product created too. The factory method decouples the operations from the concrete products, i.e., the operations don't know the type of Mario on which they are working!*
We've already seen the problem and its inefficient solutions. So let's go straight into a quick solution using the factory method.
We create the product interface — MARIO. Then, we implement its subclasses: concrete products — FireMario and CapeMario.
Now, we create the creator class —MarioMaker which declares a factory method(CreateMario()) for creating concrete products and operational methods (Operations()) to work on the created products. Again note that we can give default implementation of CreateMario() in MarioMaker itself, or we can leave it as a completely abstract method which implies that MarioMaker does not need to be an interface always; it can be a base class or an abstract class too.
One important point is that this pattern will work when the base creator class (here, the MarioMaker class) works with the product interface (MARIO). By doing so, the methods in MarioMaker do not depend on the concrete products.
Finally, we create the GameManager class that will use MarioMaker and MARIO classes only, i.e., base class and/or interfaces only. Based on the system requirements, we pass appropriate concrete creator to the StartGame() method of GameManager through which we create the concrete product.
After discussing many approaches and their drawbacks, we can say that we should use the factory method when
Some popular uses of factory method creational design pattern can be found
Factory method pattern is a creational design pattern that should be used when a class can't anticipate the class of objects it must create.
We also saw different implementations of factory methods, for instance — an abstract factory method non-abstract factory method. A parameterized factory method is also a possibility. (Explore!)
Our discussion shows that using the factory method under appropriate circumstances results in a flexible, easy-to-maintain, and reliable code that follows various Object-Oriented design principles.
Enjoy learning, Enjoy oops!