0
0
Software Engineeringknowledge~6 mins

Behavioral patterns (Observer, Strategy, Command) in Software Engineering - Full Explanation

Choose your learning style9 modes available
Introduction
Imagine you want different parts of a program to work together smoothly without being tightly connected. Behavioral patterns help solve this by organizing how objects communicate and change behavior, making programs easier to manage and extend.
Explanation
Observer Pattern
This pattern lets one object (called the subject) keep a list of other objects (observers) that want to know when something changes. When the subject changes, it automatically tells all its observers. This helps keep parts of a program updated without them being tightly linked.
Observer pattern allows objects to watch and react to changes in another object without tight connections.
Strategy Pattern
Strategy pattern lets you choose different ways to do something at runtime. Instead of hardcoding one method, you define a family of algorithms and switch between them easily. This makes programs flexible and easier to change without rewriting code.
Strategy pattern enables swapping algorithms or behaviors dynamically to keep code flexible.
Command Pattern
Command pattern turns requests or actions into objects. This means you can store, queue, or undo actions easily. It separates the object that asks for something to be done from the one that actually does it, improving control over operations.
Command pattern encapsulates actions as objects to allow flexible execution and control.
Real World Analogy

Imagine a news agency (subject) that sends updates to subscribers (observers) whenever news breaks. A chef (context) can choose different recipes (strategies) to cook a meal depending on the occasion. And a remote control (command) sends instructions to a TV to change channels or volume without knowing how the TV works inside.

Observer Pattern → News agency sending updates to subscribers who want to stay informed
Strategy Pattern → Chef choosing different recipes to prepare meals based on the event
Command Pattern → Remote control sending commands to a TV without knowing its internal workings
Diagram
Diagram
┌─────────────┐       notifies       ┌─────────────┐
│   Subject   │─────────────────────▶│  Observer   │
└─────────────┘                      └─────────────┘
       ▲                                  ▲
       │                                  │
       │                                  │
┌─────────────┐                   ┌─────────────┐
│  Context    │ uses strategy      │ Strategy    │
│ (Client)    │──────────────────▶│ Interface   │
└─────────────┘                   └─────────────┘

┌─────────────┐       executes       ┌─────────────┐
│  Invoker    │────────────────────▶│  Command    │
└─────────────┘                      └─────────────┘
                                       │
                                       ▼
                                ┌─────────────┐
                                │ Receiver    │
                                └─────────────┘
Diagram showing Subject notifying Observers, Context using Strategy, and Invoker executing Command to Receiver.
Key Facts
Observer PatternAllows objects to subscribe and get notified automatically when another object changes.
Strategy PatternDefines a family of algorithms and lets clients choose which one to use at runtime.
Command PatternEncapsulates a request as an object to allow parameterizing clients with queues, requests, and operations.
Loose CouplingDesign principle where components interact with minimal knowledge of each other.
EncapsulationWrapping data and methods into a single unit or class.
Code Example
Software Engineering
from abc import ABC, abstractmethod

# Observer Pattern
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(ABC):
    @abstractmethod
    def update(self, state):
        pass

class ConcreteObserver(Observer):
    def __init__(self, name):
        self.name = name

    def update(self, state):
        print(f"{self.name} received state update: {state}")

# Strategy Pattern
class Strategy(ABC):
    @abstractmethod
    def execute(self, data):
        pass

class ConcreteStrategyA(Strategy):
    def execute(self, data):
        return sorted(data)

class ConcreteStrategyB(Strategy):
    def execute(self, data):
        return sorted(data, reverse=True)

class Context:
    def __init__(self, strategy):
        self._strategy = strategy

    def set_strategy(self, strategy):
        self._strategy = strategy

    def do_algorithm(self, data):
        return self._strategy.execute(data)

# Command Pattern
class Command(ABC):
    @abstractmethod
    def execute(self):
        pass

class Receiver:
    def action(self):
        print("Receiver: Action performed")

class ConcreteCommand(Command):
    def __init__(self, receiver):
        self._receiver = receiver

    def execute(self):
        self._receiver.action()

class Invoker:
    def __init__(self):
        self._commands = []

    def store_command(self, command):
        self._commands.append(command)

    def execute_commands(self):
        for command in self._commands:
            command.execute()

# Demonstration
if __name__ == "__main__":
    # Observer
    subject = Subject()
    observer1 = ConcreteObserver("Observer1")
    observer2 = ConcreteObserver("Observer2")
    subject.attach(observer1)
    subject.attach(observer2)
    subject.set_state("State 1")

    # Strategy
    data = [3, 1, 2]
    context = Context(ConcreteStrategyA())
    print("Strategy A result:", context.do_algorithm(data))
    context.set_strategy(ConcreteStrategyB())
    print("Strategy B result:", context.do_algorithm(data))

    # Command
    receiver = Receiver()
    command = ConcreteCommand(receiver)
    invoker = Invoker()
    invoker.store_command(command)
    invoker.execute_commands()
OutputSuccess
Common Confusions
Thinking Observer pattern means the subject controls the observers directly.
Thinking Observer pattern means the subject controls the observers directly. In Observer pattern, the subject only notifies observers; it does not control their behavior or state.
Believing Strategy pattern changes the code inside algorithms dynamically.
Believing Strategy pattern changes the code inside algorithms dynamically. Strategy pattern switches between fixed algorithms; it does not modify the algorithms themselves at runtime.
Assuming Command pattern is just about calling methods.
Assuming Command pattern is just about calling methods. Command pattern wraps requests as objects, enabling features like undo, queuing, and logging, beyond simple method calls.
Summary
Behavioral patterns help organize how objects communicate and change behavior without tight connections.
Observer pattern keeps objects updated automatically when something changes.
Strategy pattern allows choosing different algorithms at runtime for flexibility.
Command pattern wraps actions as objects to enable flexible execution and control.