# Design Patterns

#### 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)

```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://lukasz-nowakiewicz.gitbook.io/resources/python/design-patterns.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
