SE Notes
Reusable solutions to common software design problems.
Design patterns are proven, reusable solutions to commonly occurring problems in software design. They are not finished code that can be copied directly into your program, but rather templates or blueprints for solving design problems that you can customize to fit your specific situation. Think of them as best practices codified into named solutions—when you say "use the Observer pattern here," every experienced developer immediately understands the structure, participants, and tradeoffs involved without lengthy explanation.
Origin and History
The concept of design patterns originated in architecture—Christopher Alexander's 1977 book "A Pattern Language" catalogued recurring solutions to architectural design problems. In 1994, Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides (the "Gang of Four" or GoF) published "Design Patterns: Elements of Reusable Object-Oriented Software," which catalogued 23 patterns for object-oriented software design. This book became one of the most influential texts in software engineering and established design patterns as a standard part of the developer's vocabulary.
Why Design Patterns Matter
Shared Vocabulary: Patterns provide named solutions that developers recognize. Saying "use a Factory here" communicates more design intent in two words than a paragraph of explanation.
Proven Solutions: Patterns have been tested in countless real systems. Using a pattern means benefiting from the collective experience of many developers who faced similar problems.
Design Quality: Patterns embody principles like loose coupling, high cohesion, and programming to interfaces. Using them naturally produces more flexible, maintainable code.
Learning Tool: Studying patterns teaches design thinking—understanding why certain structures work better than others develops judgment applicable beyond the specific patterns.
Pattern Categories
The GoF organized patterns into three categories:
Creational Patterns (Object Creation)
Control how objects are created, hiding creation logic rather than instantiating objects directly:
Singleton: Ensures a class has only one instance and provides a global access point. Used for database connection pools, configuration managers, and logging services.
class DatabasePool:
_instance = None
@classmethod
def get_instance(cls):
if cls._instance is None:
cls._instance = cls()
return cls._instanceFactory Method: Defines an interface for creating objects but lets subclasses decide which class to instantiate. Used when the exact type of object needed depends on context.
Abstract Factory: Creates families of related objects without specifying their concrete classes. Used for cross-platform UI toolkits where you need matching sets of components.
Builder: Constructs complex objects step by step, allowing the same construction process to create different representations. Used for objects with many optional parameters.
Prototype: Creates new objects by cloning existing ones. Used when object creation is expensive and similar objects are needed.
Structural Patterns (Object Composition)
Deal with object composition—how classes and objects are combined to form larger structures:
Adapter: Converts the interface of a class into another interface that clients expect. Like a power adapter that lets a US plug work in a European socket.
Decorator: Dynamically adds behavior to objects without altering their structure. Like wrapping a gift—each wrapper adds something without changing the gift inside.
Facade: Provides a simplified interface to a complex subsystem. A single "placeOrder()" method might internally coordinate inventory, payment, shipping, and notification subsystems.
Proxy: Provides a surrogate or placeholder for another object to control access. Used for lazy loading, caching, access control, and remote service access.
Composite: Composes objects into tree structures where individual objects and compositions are treated uniformly. Used for file systems (files and folders), UI components, and organization hierarchies.
Behavioral Patterns (Object Communication)
Concerned with algorithms and communication between objects:
Observer: Defines a one-to-many dependency so that when one object changes state, all dependents are notified automatically. Used for event systems, UI updates, and pub/sub messaging.
class EventEmitter:
def __init__(self):
self._listeners = {}
def on(self, event, callback):
self._listeners.setdefault(event, []).append(callback)
def emit(self, event, data):
for callback in self._listeners.get(event, []):
callback(data)Strategy: Defines a family of algorithms, encapsulates each one, and makes them interchangeable. The algorithm varies independently of clients that use it.
Command: Encapsulates a request as an object, enabling parameterization, queuing, logging, and undo operations.
State: Allows an object to alter its behavior when its internal state changes. The object appears to change its class.
Template Method: Defines the skeleton of an algorithm, deferring some steps to subclasses. Subclasses redefine certain steps without changing the algorithm's structure.
When to Use Patterns
Patterns are solutions to problems—apply them when you face the problem they solve, not proactively "just in case." Warning signs that a pattern might help: duplicated code that varies slightly (Template Method or Strategy), complex conditional logic selecting behavior (State or Strategy), tightly coupled classes that change together (Observer or Mediator), and objects that are expensive to create or need controlled access (Factory, Singleton, Proxy).
Anti-Patterns
Just as design patterns describe proven solutions, anti-patterns describe commonly occurring mistakes: God Object (one class doing everything), Spaghetti Code (tangled, unstructured logic), Golden Hammer (applying a favorite pattern everywhere regardless of fit), and Pattern Mania (using patterns unnecessarily where simple code suffices).
Real-World Application
Most modern frameworks are built on design patterns: MVC (Model-View-Controller) combines Observer, Strategy, and Composite. Spring Framework uses Factory, Dependency Injection, Proxy, and Template Method extensively. React uses Observer (state changes trigger re-renders), Composite (component trees), and Strategy (render props and hooks).
Exam Focus
Revise definitions, diagrams, examples, and short-answer points for Design Patterns Introduction — Software Engineering.
Interview Use
Prepare one clear explanation, one practical example, and one common mistake for this Software Engineering topic.
Search Terms
software-engineering, software engineering, software, engineering, design, patterns, introduction, design patterns introduction — software engineering
Related Software Engineering Topics