Behavioral patterns (Observer, Strategy, Command) in Software Engineering - Time & Space Complexity
Behavioral design patterns help organize how objects interact and change behavior. Analyzing their time complexity shows how the cost of these interactions grows as the number of objects or commands increases.
We want to understand how the execution time changes when using Observer, Strategy, or Command patterns as the system grows.
Analyze the time complexity of notifying observers in the Observer pattern.
class Subject:
def __init__(self):
self.observers = []
def register(self, observer):
self.observers.append(observer)
def notify(self, message):
for observer in self.observers:
observer.update(message)
This code shows a subject notifying all registered observers by calling their update method.
Look at what repeats when notify is called.
- Primary operation: Looping through all observers to call update.
- How many times: Once for each observer registered.
As the number of observers grows, the notify method calls update more times.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | 10 update calls |
| 100 | 100 update calls |
| 1000 | 1000 update calls |
Pattern observation: The number of operations grows directly with the number of observers.
Time Complexity: O(n)
This means the time to notify observers grows linearly with how many observers there are.
[X] Wrong: "Notifying observers happens instantly no matter how many there are."
[OK] Correct: Each observer must be updated one by one, so more observers mean more work and more time.
Understanding how behavioral patterns affect time helps you explain design choices clearly. It shows you can think about both design and performance, a valuable skill in real projects.
What if the notify method used multiple threads to update observers in parallel? How would the time complexity change?
Practice
Solution
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.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.Final Answer:
Observer -> Option CQuick Check:
Notification to many = Observer [OK]
- Confusing Strategy with Observer
- Thinking Command handles notifications
- Mixing Singleton with behavioral patterns
Solution
Step 1: Identify Command pattern structure
The Command pattern encapsulates a request as an object, typically by defining an interface with anexecute()method to perform the action.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.Final Answer:
An interface with a method like execute() to perform the action -> Option AQuick Check:
Command interface = execute() method [OK]
- Confusing Command with Strategy interface
- Thinking Command notifies observers
- Mixing Singleton with Command
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?
Solution
Step 1: Understand the Strategy pattern usage
The Context uses StrategyA, whosedo_algorithmmethod sorts the list in ascending order.Step 2: Trace the execution
Callingcontext.execute([3, 1, 2])callsStrategyA.do_algorithmwhich returns[1, 2, 3].Final Answer:
[1, 2, 3] -> Option BQuick Check:
StrategyA sorts ascending = [1, 2, 3] [OK]
- Assuming StrategyB is used
- Confusing sorting order
- Expecting original list unchanged
Solution
Step 1: Understand observer detachment importance
If observers are not detached, the subject keeps references to them, preventing garbage collection.Step 2: Identify consequence
This causes memory leaks because unused observers remain in memory unnecessarily.Final Answer:
Memory leaks due to lingering references -> Option AQuick Check:
Undetached observers cause memory leaks [OK]
- Confusing with Strategy or Command issues
- Thinking it causes command execution failure
- Assuming Singleton duplication
Solution
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.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.Final Answer:
Command pattern, because it encapsulates requests as objects allowing queuing and undo -> Option DQuick Check:
Queue and undo operations = Command pattern [OK]
- Choosing Observer for queuing commands
- Confusing Strategy with Command
- Selecting Singleton for operation management
Ready to prove it?
Pass the test and this pattern moves unknown
