Design Patterns

Examples provided by ChatGPT.

Design Patterns Examples

Here are some examples of design patterns in Python:

Factory Pattern

class Shape:
    def draw(self):
        pass
    
class Circle(Shape):
    def draw(self):
        print("Drawing a circle")
    
class Rectangle(Shape):
    def draw(self):
        print("Drawing a rectangle")

class ShapeFactory:
    def get_shape(self, shape_type: str):
        if shape_type == 'circle':
            return Circle()
        elif shape_type == 'rectangle':
            return Rectangle()
        else:
            raise ValueError('Invalid shape type')

In the above example, ShapeFactory creates objects of Circle or Rectangle based on the input parameter shape_type.

Observer Pattern

The Observer pattern is used to establish a one-to-many dependency between objects. When the state of one object changes, all its dependents are notified and updated automatically.

class Subject:
    def __init__(self):
        self.observers = []
    
    def attach(self, observer):
        self.observers.append(observer)
    
    def detach(self, observer):
        self.observers.remove(observer)
    
    def notify(self):
        for observer in self.observers:
            observer.update(self)

class Observer:
    def update(self, subject):
        pass

class ConcreteObserver(Observer):
    def update(self, subject):
        print('Subject state has changed:', subject.state)

class ConcreteSubject(Subject):
    def __init__(self):
        super().__init__()
        self.state = None
    
    def set_state(self, state):
        self.state = state
        self.notify()

In the above example, Subject maintains a list of Observer objects and notifies them when its...

class Subject:
    def __init__(self):
        self.observers = []
        self.state = None
    
    def attach(self, observer):
        self.observers.append(observer)
    
    def detach(self, observer):
        self.observers.remove(observer)
    
    def notify(self):
        for observer in self.observers:
            observer.update(self.state)
    
    def set_state(self, state):
        self.state = state
        self.notify()

class Observer:
    def update(self, state):
        pass

class ConcreteObserverA(Observer):
    def update(self, state):
        print(f'ConcreteObserverA received state: {state}')

class ConcreteObserverB(Observer):
    def update(self, state):
        print(f'ConcreteObserverB received state: {state}')

In the above example, Subject is a class that maintains a list of Observer objects and notifies them when its state changes. attach and detach methods are used to add and remove observers from the list. notify method iterates over the list of observers and calls their update method with the current state. set_state method sets the current state of the subject and calls notify to update all observers.

Observer is an abstract class that defines the interface for objects that should be notified when the subject changes. ConcreteObserverA and ConcreteObserverB inherit from Observer and implement the update method to define their own behavior when notified.

Here is an example of how to use the Observer pattern:

subject = Subject()
observer_a = ConcreteObserverA()
observer_b = ConcreteObserverB()

subject.attach(observer_a)
subject.attach(observer_b)

subject.set_state('new state')
# Output:
# ConcreteObserverA received state: new state
# ConcreteObserverB received state: new state

subject.detach(observer_b)

subject.set_state('another state')
# Output:
# ConcreteObserverA received state: another state

In the above example, we create a Subject object and two observers, ConcreteObserverA and ConcreteObserverB. We attach both observers to the subject and then change its state using set_state. Both observers are notified and print the new state. We then detach ConcreteObserverB from the subject and change its state again. Only ConcreteObserverA is notified this time.

Singleton Pattern

class Singleton:
    __instance = None
    
    def __new__(cls):
        if cls.__instance is None:
            cls.__instance = super().__new__(cls)
        return cls.__instance

In the above example, Singleton is a class that can only have one instance. The __new__ method ensures that if an instance of Singleton already exists, it will return that instance instead of creating a new one.

Decorator Pattern

class Component:
    def operation(self):
        pass

class ConcreteComponent(Component):
    def operation(self):
        print('ConcreteComponent operation')

class Decorator(Component):
    def __init__(self, component: Component):
        self.component = component
    
    def operation(self):
        self.component.operation()

class ConcreteDecoratorA(Decorator):
    def operation(self):
        super().operation()
        print('ConcreteDecoratorA operation')

class ConcreteDecoratorB(Decorator):
    def operation(self):
        super().operation()
        print('ConcreteDecoratorB operation')

In the above example, Component is an interface that defines the base functionality of an object. ConcreteComponent implements the interface. Decorator is an abstract class that implements the same interface and holds a reference to a Component. ConcreteDecoratorA and ConcreteDecoratorB inherit from Decorator and add their own functionality to the base operation.

Strategy Pattern

class Strategy:
    def execute(self):
        pass

class ConcreteStrategyA(Strategy):
    def execute(self):
        print('Executing ConcreteStrategyA')

class ConcreteStrategyB(Strategy):
    def execute(self):
        print('Executing ConcreteStrategyB')

class Context:
    def __init__(self, strategy: Strategy):
        self.strategy = strategy
    
    def execute_strategy(self):
        self.strategy.execute()

In the above example, Strategy is an interface that defines the algorithm that will be used by the Context object. ConcreteStrategyA and ConcreteStrategyB implement the interface with different algorithms. Context holds a reference to a Strategy object and executes its algorithm using execute_strategy.

Template Method Pattern

class AbstractClass:
    def template_method(self):
        self.operation1()
        self.operation2()
    
    def operation1(self):
        pass
    
    def operation2(self):
        pass

class ConcreteClassA(AbstractClass):
    def operation1(self):
        print('ConcreteClassA operation1')
    
    def operation2(self):
        print('ConcreteClassA operation2')

class ConcreteClassB(AbstractClass):
    def operation1(self):
        print('ConcreteClassB operation1')
    
    def operation2(self):
        print('ConcreteClassB operation2')

In the above example, AbstractClass is an abstract class that defines a template method that calls two abstract methods, operation1 and operation2. ConcreteClassA and ConcreteClassB inherit from AbstractClass and implement the abstract methods to define their own behavior.

Iterator Pattern

class Iterator:
    def __init__(self, collection):
        self.collection = collection
        self.index = 0
    
    def has_next(self):
        return self.index < len(self.collection)
    
    def next(self):
        value = self.collection[self.index]
        self.index += 1
        return value

class Collection:
    def __init__(self):
        self.items = []
    
    def add_item(self, item):
        self.items.append(item)
    
    def get_iterator(self):
        return Iterator(self.items)

Last updated