Design Patters: Factory Method

(Credit: Concepts explained here are based on a book, “増補改訂版Java言語で学ぶデザインパターン入門”.)

A factory method pattern is a pattern in which a superclass describes how an instance should be created but does not define its internal structure. Its detailed structure is written in a subclass.

Let’s create a program. In this program, a framework package is used for creating an instance and a idcard package is used for adding an internal structure or details of that class such as method and attributes.

First class in this program is a Product class. This class only defines a method, use, that is implemented in its subclass. This class basically defines a product as something you can ‘use’.

# product.py
from abc import ABC, abstractmethod

class Product(ABC):
    @abstractmethod
    def use(self):
        pass

Next is a Factory class. This class defines three methods; create, create_product and register_product. Create is a class that creates and registers an instance of a product. create_product is a method to create an instance of a Product class. register_product is a method that registers an owner of a created product. Both create_product and register_product are abstract methods so they are implemented in its subclass.

# factory.py
from abc import ABC, abstractmethod
from framework import Product

class Factory(ABC):
    def create(self, owner):
        p = self._create_product(owner)
        self._register_product(p)
        assert isinstance(p, Product)
        return p
    
    @abstractmethod
    def _create_product(self, owner):
        pass

    @abstractmethod
    def _register_product(self, product):
        pass

Let’s move on to an idcard package. Let’s create an actual product, an id card.

# idcard.py
from framework import Product

class IDCard(Product):
    def __init__(self, owner):
        print(f"Create {owner}'s card. '")
        self.owner = owner

    def use(self):
        print(f"Use {self.owner}'s card. '")

    def get_owner(self):
        return self.owner

Next class is a IDCardFactory class which has two methods: create_product and register_product. create_product method creates an instance of an IDCard class. register_product registers an owner of a created product.

# idcard_factory.py
from framework import Product
from framework import Factory
from idcard import IDCard

class IDCardFactory(Factory):
    _owners = []

    def _create_product(self, owner):
        return IDCard(owner)

    def _register_product(self, product):
        self._owners.append(product.get_owner())

    def get_owners(self):
        return self._owners

Let’s run this program.

# main.py
from framework import Product, Factory
from idcard import IDCard, IDCardFactory


def main():
    factory = IDCardFactory()
    card1 = factory.create("Kenta")
    card2 = factory.create("test-user")
    card3 = factory.create("Mr X")
    card1.use()
    card2.use()
    card3.use()
    print(f"Registered owners are {factory.get_owners()}")

if __name__ == "__main__":
    main()
# Result of running main.py
Create Kenta's card. '
Create test-user's card. '
Create Mr X's card. '
Use Kenta's card. '
Use test-user's card. '
Use Mr X's card. '
Registered owners are ['Kenta', 'test-user', 'Mr X']

One benefit of a Factory method pattern: You only need to modify a concrete package which is an idcard package in this program without modifying a framework package. Since a framework package does not depend on a concrete package or a package that makes a structure of a product, modifying or adding a new product without affecting a framework package is possible. For example, it is possible to create a computer package and to generates and to register a computer object without touching source code used in a framework package.

Leave a comment