Bird
Raised Fist0
Software Engineeringknowledge~6 mins

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

Choose your learning style10 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 Pattern → Allows objects to subscribe and get notified automatically when another object changes.
Strategy Pattern → Defines a family of algorithms and lets clients choose which one to use at runtime.
Command Pattern → Encapsulates a request as an object to allow parameterizing clients with queues, requests, and operations.
Loose Coupling → Design principle where components interact with minimal knowledge of each other.
Encapsulation → Wrapping 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.

Practice

(1/5)
1. Which behavioral pattern is used when an object needs to notify multiple other objects about a change in its state?
easy
A. Command
B. Strategy
C. Observer
D. Singleton

Solution

  1. Step 1: Understand the role of Observer pattern

    The Observer pattern allows one object (the subject) to notify many dependent objects (observers) automatically when its state changes.
  2. Step 2: Compare with other patterns

    Strategy selects algorithms at runtime, Command encapsulates requests, and Singleton restricts instance creation. Only Observer fits the notification role.
  3. Final Answer:

    Observer -> Option C
  4. Quick Check:

    Notification to many = Observer [OK]
Hint: Notification to many objects? Think Observer pattern [OK]
Common Mistakes:
  • Confusing Strategy with Observer
  • Thinking Command handles notifications
  • Mixing Singleton with behavioral patterns
2. Which of the following is the correct way to define a Command pattern interface in object-oriented design?
easy
A. An interface with a method like execute() to perform the action
B. A class that holds multiple algorithms to choose from
C. A subject that notifies observers about changes
D. A singleton class that manages global state

Solution

  1. Step 1: Identify Command pattern structure

    The Command pattern encapsulates a request as an object, typically by defining an interface with an execute() method to perform the action.
  2. Step 2: Eliminate other options

    A class that holds multiple algorithms to choose from describes Strategy, C describes Observer, and D describes Singleton, none of which define a command interface.
  3. Final Answer:

    An interface with a method like execute() to perform the action -> Option A
  4. Quick Check:

    Command interface = execute() method [OK]
Hint: Command pattern always has an execute() method [OK]
Common Mistakes:
  • Confusing Command with Strategy interface
  • Thinking Command notifies observers
  • Mixing Singleton with Command
3. Consider this code snippet using the Strategy pattern:
class Context:
    def __init__(self, strategy):
        self.strategy = strategy
    def execute(self, data):
        return self.strategy.do_algorithm(data)

class StrategyA:
    def do_algorithm(self, data):
        return sorted(data)

class StrategyB:
    def do_algorithm(self, data):
        return sorted(data, reverse=True)

context = Context(StrategyA())
result = context.execute([3, 1, 2])
print(result)

What will be the output?
medium
A. [3, 1, 2]
B. [1, 2, 3]
C. [3, 2, 1]
D. TypeError

Solution

  1. Step 1: Understand the Strategy pattern usage

    The Context uses StrategyA, whose do_algorithm method sorts the list in ascending order.
  2. Step 2: Trace the execution

    Calling context.execute([3, 1, 2]) calls StrategyA.do_algorithm which returns [1, 2, 3].
  3. Final Answer:

    [1, 2, 3] -> Option B
  4. Quick Check:

    StrategyA sorts ascending = [1, 2, 3] [OK]
Hint: StrategyA sorts ascending, so output is sorted list [OK]
Common Mistakes:
  • Assuming StrategyB is used
  • Confusing sorting order
  • Expecting original list unchanged
4. In the Observer pattern, what is the main issue if observers are not properly detached when no longer needed?
medium
A. Memory leaks due to lingering references
B. Strategy selection becomes unpredictable
C. Commands fail to execute
D. Singleton instance is duplicated

Solution

  1. Step 1: Understand observer detachment importance

    If observers are not detached, the subject keeps references to them, preventing garbage collection.
  2. Step 2: Identify consequence

    This causes memory leaks because unused observers remain in memory unnecessarily.
  3. Final Answer:

    Memory leaks due to lingering references -> Option A
  4. Quick Check:

    Undetached observers cause memory leaks [OK]
Hint: Undetached observers cause memory leaks [OK]
Common Mistakes:
  • Confusing with Strategy or Command issues
  • Thinking it causes command execution failure
  • Assuming Singleton duplication
5. You want to design a system where users can perform different operations (like save, load, delete) on files, and you want to queue these operations to execute later or undo them if needed. Which behavioral pattern best fits this requirement and why?
hard
A. Singleton pattern, because it ensures only one file manager exists
B. Observer pattern, because it notifies users about file changes
C. Strategy pattern, because it selects the best file operation algorithm
D. Command pattern, because it encapsulates requests as objects allowing queuing and undo

Solution

  1. Step 1: Analyze the requirement for queuing and undo

    Queuing operations and supporting undo requires encapsulating each request as an object to store and manage them flexibly.
  2. Step 2: Match with behavioral patterns

    The Command pattern encapsulates requests as objects, enabling queuing, logging, and undo functionality. Observer and Strategy do not support queuing commands, and Singleton only controls instance creation.
  3. Final Answer:

    Command pattern, because it encapsulates requests as objects allowing queuing and undo -> Option D
  4. Quick Check:

    Queue and undo operations = Command pattern [OK]
Hint: Queue and undo operations? Use Command pattern [OK]
Common Mistakes:
  • Choosing Observer for queuing commands
  • Confusing Strategy with Command
  • Selecting Singleton for operation management
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard