G.O.F. book says following about Factory Method Pattern :
Intent Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.
Applicability Use the Factory Method pattern when
- a class can't anticipate the class of objects it must create.
- a class wants its subclasses to specify the objects it creates.
- classes delegate responsibility to one of several helper subclasses, and you want to localize the knowledge of which helper subclass is the delegate
In [12]:
# code and comments inspired from : https://refactoring.guru/design-patterns/factory-method/python/example
from abc import ABC, abstractmethod
class Being(ABC):
"""
The Being interface declares the operations that all concrete beings
must implement.
"""
@abstractmethod
def talk(self) -> str:
pass
"""
Concrete Products provide various implementations of the Product interface.
"""
class CityBeing(Being):
def talk(self) -> str:
return "{City Talk : Sorry, Thankyou, I dont have time}"
class WildBeing(Being):
def talk(self) -> str:
return "{Wild Talk : Get lost, I hate you, I love you}"
class BeingCreator(ABC):
"""
The BeingCreator class declares the factory method that is supposed to return an
object of a Being class. The BeingCreator's subclasses usually provide the
implementation of this method.
"""
def make_noise(self) -> str:
"""
Despite its name, the BeingCreator's primary responsibility
is not creating Beings. Usually, it contains some core business logic
like make_noise, that relies on Product objects, returned by the factory method.
Subclasses can indirectly change that business logic by overriding the
factory method and returning a different type of Being from it.
"""
# Call the factory method to create a Being object.
being = self.create_being()
# Now, use the Being.
result = f"BeingCreator: The same creator's code has \
just worked with {being.talk()} ! {being.talk()} ! {being.talk()} !"
return result
@abstractmethod
def create_being(self):
"""
Note that the BeingCreator may also provide some default implementation of
this factory method.
"""
pass
"""
Concrete BeingCreators override the factory method in order to change the resulting
product's type.
"""
class CityBeingCreator(BeingCreator):
"""
Note that the signature of the method still uses the abstract Being type,
even though the concrete being is actually returned from the method. This
way the BeingCreator can stay independent of concrete being classes.
"""
def create_being(self) -> Being:
return CityBeing()
class WildBeingCreator(BeingCreator):
def create_being(self) -> Being:
return WildBeing()
In [13]:
def client_code(creator: BeingCreator) -> None:
"""
The client code works with an instance of a concrete being creator, albeit through
its base interface. As long as the client keeps working with the being creator via
the base interface, you can pass it any being creator's subclass.
"""
print(f"Client: I'm not aware of the being creator's class, but it still works.\n"
f"{creator.make_noise()}", end="")
if __name__ == "__main__":
print("App: Launched with the CityBeingCreator.")
client_code(CityBeingCreator())
print("\n")
print("App: Launched with the WildBeingCreator.")
client_code(WildBeingCreator())