Consider the following python3 code:
from abc import ABC, abstractmethod class Food(ABC): _food_factory_map = {} _recipes = {} @classmethod def getFood(cls, foodName): return cls._food_factory_map[foodName]() @classmethod def registerFood(cls, foodName, recipe): def callable(foodClass): cls._food_factory_map[foodName] = foodClass cls._recipes[foodName] = recipe return foodClass return callable def __init__(self): print("Starting Food prep") @abstractmethod def prepareFood(self): ... def serveFood(self): food = self.prepareFood() print(food) @Food.registerFood('Burger', 'Burger Recipe') class Burger(Food): def prepareFood(self): return 'Burger is ready!' @Food.registerFood('Pizza', 'Pizza Recipe') class Pizza(Food): def prepareFood(self): return 'Pizza is ready!' if __name__ == '__main__': fs = ('Burger', 'Pizza') for f in fs: print(f"Food Recipe: {Food._recipes[f]}") food = Food.getFood(f) food.serveFood() Here the abstract base class Food has class methods that implement the Factory design pattern. A client can register their own concrete classes with registerFood and retrieve them with getFood. The abstract base class also implements other abstract and concrete methods to define the interface.
One can also separate out the two class methods (getFood and registerFood) and the two class members (_food_factory_map and _recipes) into a class of it's own (FoodFactory)
class FoodFactory(ABC): _food_factory_map = {} _recipes = {} @classmethod def getFood(cls, foodName): return cls._food_factory_map[foodName]() @classmethod def registerFood(cls, foodName, recipe): def callable(foodClass): cls._food_factory_map[foodName] = foodClass cls._recipes[foodName] = recipe return foodClass return callable All the client code will then change to using FoodFactory instead of Food.
@FoodFactory.registerFood('Pizza', 'Pizza Recipe') class Pizza(Food): def prepareFood(self): return 'Pizza is ready!' if __name__ == '__main__': fs = ('Burger', 'Pizza') for f in fs: print(f"Food Recipe: {FoodFactory._recipes[f]}") food = FoodFactory.getFood(f) food.serveFood() Which of these implementations is better and why? Or is it perfectly reasonable to use either? Is it language dependent?
One advantage I can think off is that with the second method, one can override the __new__ of FoodFactory to directly return an instance of the appropriate food rather than calling a separate getFood method. But that difference seems largely cosmetic.