Skip to main content

Creational

Factory

    • Object Creation without Exposing Concrete Classes: Use the Factory pattern when you want to create objects without exposing the concrete classes to the client code. Factories provide a centralized way to create objects, allowing for better encapsulation and abstraction.
    • Dynamic Object Creation: Factories are useful when the exact class of the object to be created may vary at runtime based on certain conditions or configurations. Factories can select the appropriate subclass or implementation to create based on these conditions.
    • Polymorphic Object Creation: Factories enable polymorphic object creation, allowing clients to create objects without knowing the specific subclass or implementation being instantiated. This promotes loose coupling and simplifies client code.
class Burger:
    def __init__(self, ingredients):
        self.ingredients = ingredients

    def print(self):
        print(self.ingredients)

class BurgerFactory:
    
    def createCheeseBurger(self):
        ingredients = ["bun", "cheese", "beef-patty"]
        return Burger(ingredients)
    
    def createDeluxeCheeseBurger(self):
        ingredients = ["bun", "tomatoe", "lettuce", "cheese", "beef-patty"]
        return Burger(ingredients)

    def createVeganBurger(self):
        ingredients = ["bun", "special-sauce", "veggie-patty"]
        return Burger(ingredients)

burgerFactory = BurgerFactory()
burgerFactory.createCheeseBurger().print()
burgerFactory.createDeluxeCheeseBurger().print()
burgerFactory.createVeganBurger().print()

Output

['bun', 'cheese', 'beef-patty']

 

['bun', 'tomatoe', 'lettuce', 'cheese', 'beef-patty']

 

['bun', 'special-sauce', 'veggie-patty']

Builder

class Burger:
    def __init__(self):
        self.buns = None
        self.patty = None
        self.cheese = None

    def setBuns(self, bunStyle):
        self.buns = bunStyle
    
    def setPatty(self, pattyStyle):
        self.patty = pattyStyle
    
    def setCheese(self, cheeseStyle):
        self.cheese = cheeseStyle

class BurgerBuilder:
    def __init__(self):
        self.burger = Burger()

    def addBuns(self, bunStyle):
        self.burger.setBuns(bunStyle)
        return self
    
    def addPatty(self, pattyStyle):
        self.burger.setPatty(pattyStyle)
        return self
    
    def addCheese(self, cheeseStyle):
        self.burger.setCheese(cheeseStyle)
        return self  

    def build(self):
        return self.burger

burger = BurgerBuilder() \
            .addBuns("sesame") \
            .addPatty("fish-patty") \
            .addCheese("swiss cheese") \
            .build()

The Builder pattern and the Factory pattern are both creational design patterns used to create objects, but they serve different purposes and are used in different scenarios. Here's when you might choose one over the other:

  1. Builder Pattern:

    • Complex Object Construction: Use the Builder pattern when you need to create complex objects with multiple optional or mandatory parameters, and the number of parameters makes constructor overloading impractical or confusing.
    • Fluent Interface: The Builder pattern often uses a fluent interface, allowing for a more expressive and readable way to construct objects, especially when chaining multiple method calls together.
    • Flexible Object Creation: Builders can be used to create objects in a step-by-step manner, allowing for more flexibility in the construction process. This can be useful when different parts of the object need to be configured independently or when the construction process is dynamic.
    • Parameter Omission: Builders allow parameters to be omitted or set to default values, providing more control over object creation and reducing the need for multiple overloaded constructors

In summary, use the Builder pattern when you need to create complex objects with multiple parameters in a flexible and expressive manner, and use the Factory pattern when you need to encapsulate object creation, provide abstraction, and support dynamic or polymorphic object creation. Consider the complexity of the object being created and the level of control and flexibility needed in the construction process when choosing between the two patterns.


Singleton

class ApplicationState:
    instance = None

    def __init__(self):
        self.isLoggedIn = False

    @staticmethod
    def getAppState():
        if not ApplicationState.instance:  
            ApplicationState.instance = ApplicationState()
        return ApplicationState.instance

appState1 = ApplicationState.getAppState()
print(appState1.isLoggedIn)

appState2 = ApplicationState.getAppState()
appState1.isLoggedIn = True

print(appState1.isLoggedIn)
print(appState2.isLoggedIn)

Output

False

 

True 

True