Design patterns are reusable templates that help us solve software design problems using best practices. In this way, they help us build applications using code that is easier to maintain, understand, reuse and test.
What is this pattern for?
Defines an interface to create an object, but lets subclasses decide which class to instantiate. The factory method allows a class to defer the instantiation it uses to subclasses.
Let’s see it with an example. We are going to create a new social network and we are going to call it FaceTube. This social network has two versions, one free and one paid. On its home page, the user will see an infinite list with content, which can be of three types: Video, Post, or Ad. The paid version, however, has no ads, only videos, and posts, unlike the free version that will have the three types of content.
Now we have to program the part of the application that is in charge of filling the user’s feed with content. But the way to fill it is different depending on whether the user has the free or paid version. One option (the bad one) is to add code blocks like the following in the feed code.
if free_version: if condition1: next_item = Video() elif condition2: next_item = Post() else: next_item = Ad() elif paid_version: if condition1: next_item = Video() else: next_item = Post() else: raise NotImplementedError()
However, this creates some problems:
- The application should not care about the particular types of content it is going to display, that is the responsibility of the ContentManager.
- If we mix the application logic with the ContentManager logic, we will not be able to develop both parts independently.
- As they are mixed, every time we include a new type of content we will have to test both parts.
- Every time we include a new type of content, we will have to change the application logic in all the places where the content is instantiated.
The solution: instantiate the content using a factory method.
How does it work?
In the following diagram we can see the basic operation of this pattern:
The application will have contact with an abstract class called ContentManager, and will not care about the type of content it creates. Whether you create content for the free or paid version, the application will remain agnostic to those details.
We will also have an abstract class called Content that will represent whatever type of content is to be displayed in the app. And that content will be created with the factory method create_content() of the ContentManager.
Here you can see the abstract classes that the application will have contact with in Python:
from abc import ABC, abstractmethod class ContentManager(ABC): def __init__(self): self.content_list =  self._load_content() def _load_content(self): content = self.create_content() self.content_list.append(content) @abstractmethod def create_content(self): raise NotImplementedError() class Content(ABC): def __init__(self): pass
As you can see, ContentManager is responsible for creating and organizing content but defers the choice of content type to its subclasses. The create_content() method is the factory method for creating the different types of content, and it is the responsibility of the ContentManager subclasses to choose the content creation strategy for this method.
We are now going to create two types of ContentManager for the free and paid versions:
class FreeContentManager(ContentManager): def create_content(self): if condition1: return Post() elif condition2: return Video() else: return Ad() class PaidContentManager(ContentManager): def create_content(self): if condition: return Post() else: return Video() class Post(Content): ... class Video(Content): ... class Ad(Content): ...
As you can see, each of these subclasses will create the content following different strategies, without affecting the app’s execution. For the application, there is only the ContentManager interface that creates Content. The rest are details that we have encapsulated in the subclasses.
When the application starts, just check the version that the user is using and create the appropriate ContentManager:
if version == ‘paid’: content_manager = PaidContentManager() elif version == ‘free’: content_manager = FreeContentManager() else: raise NotImplementedError()
And in this way, each version of the app will use its own content management strategy.
- We delegate the choice of what content to create to the ContentManager subclasses.
- We can design the user feed logic without worrying about all the possible types of content the app will have.
- We avoid duplicating the content creation code in many different parts of the application.
- We avoid including details in the feed logic that are not its concern.
Escape Velocity Labs
You can find all our articles, courses, and tutorials on our website: