Codecademy Logo

Design Patterns and Architecture

Related learning

  • Learn how to apply SOLID principles, design patterns, and layered architecture with AI to design and refactor full-stack applications.
    • With Certificate
    • Intermediate.
      4 hours

Design Patterns

Design patterns are reusable solutions to commonly occurring problems in software design. The Gang of Four (Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides) catalogued 23 such patterns in 1994, naming the answers engineers had been arriving at independently. Patterns provide a shared vocabulary for recurring structural problems.

Patterns vs. Principles

Design patterns differ from design principles in that patterns are concrete solutions while principles are guidelines for decision-making. Principles describe properties a good design should have. Patterns are reusable structural blueprints. Understanding the principles behind a pattern is what makes it easier to see when the pattern actually fits.

Singleton Pattern

The Singleton pattern ensures a class has only one instance and provides a global point of access to it. It is a creational pattern with object scope, useful when shared state must remain consistent across the application. Overuse can create tight coupling, making code harder to test or swap out.

CLASS AppSettings:
instance = null -- private, shared across all callers
FUNCTION getInstance():
IF instance IS null:
instance = NEW AppSettings()
RETURN instance

Factory Method

The Factory Method pattern defines an interface for creating an object but lets subclasses decide which class to instantiate. The base class owns the workflow; subclasses fill in the creation step. New types can be added by writing a new subclass without modifying the base class.

CLASS NotificationSender:
FUNCTION createNotification() -- subclasses must implement
FUNCTION send():
notification = createNotification()
notification.deliver()
CLASS EmailSender EXTENDS NotificationSender:
FUNCTION createNotification():
RETURN NEW EmailNotification()

Observer Pattern

The Observer pattern defines a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically. The subject keeps a list of observers and broadcasts to all of them, with no direct knowledge of what each observer does with the notification.

CLASS OrderService: -- the subject
observers = []
FUNCTION addObserver(observer):
observers.append(observer)
FUNCTION placeOrder(order):
FOR each observer:
observer.update(order)

Strategy Pattern

The Strategy pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. Each variation lives in its own object behind a shared interface, and the consumer picks one at runtime without knowing which specific implementation it received. New variations are added by writing a new strategy class.

INTERFACE PaymentStrategy:
processPayment(order)
CLASS CreditCardPayment IMPLEMENTS PaymentStrategy:
FUNCTION processPayment(order) -- charge the card
CLASS WalletPayment IMPLEMENTS PaymentStrategy:
FUNCTION processPayment(order) -- deduct from wallet

Adapter Pattern

The Adapter pattern allows incompatible interfaces to work together by wrapping an interface around an existing class. When a third-party library or legacy system has a shape that does not match what calling code expects, an adapter sits between them and translates the calls, keeping the incompatibility contained.

CLASS LegacyPaymentGateway:
FUNCTION charge(amount, cardToken, currency)
INTERFACE PaymentProcessor:
processPayment(order)
CLASS PaymentAdapter IMPLEMENTS PaymentProcessor:
FUNCTION new(legacyGateway):
RETURN legacyGateway.charge(
order.total, order.cardToken, order.currency
)

Decorator Pattern

The Decorator pattern attaches additional responsibilities to an object dynamically, providing a flexible alternative to subclassing. Each decorator wraps the object beneath it, adding one responsibility while preserving the original interface. Stacking multiple decorators avoids the explosion of subclasses that would otherwise be needed for each combination of features.

CLASS Report: -- the base component
FUNCTION generate()
CLASS LoggingDecorator EXTENDS Report: -- wraps a Report
FUNCTION new(inner) -- holds the wrapped report
FUNCTION generate():
log("Generating report")
result = inner.generate()
RETURN result

Pattern Selection

Selecting the appropriate design pattern requires evaluating the problem context, the trade-offs the pattern introduces, and the potential for overengineering. KISS and YAGNI both warn against applying a pattern before its target problem actually exists. The starting point is always the problem, not a pattern that looks structurally familiar.

Layered Architecture

Layered architecture organizes code into distinct layers (presentation, business logic, and data access) to separate concerns at the system level. Each layer has a focused responsibility: presentation handles requests and responses, business logic enforces rules and workflows, and data access manages how data is read and written.

Principles at Layer Scale

Architectural layers apply the same design principles learned at the class and module level to larger system structures. Single Responsibility Principle governs which code belongs in which layer, and Dependency Inversion Principle governs how layers depend on each other. The same thinking that shaped individual modules now shapes whole layers.

Pattern Categories

Design patterns fall into three categories. Creational patterns control how objects are created. Structural patterns define how components fit together. Behavioral patterns govern how objects communicate and divide responsibility. Each category names a distinct kind of design problem the patterns within it address.

Pattern Scope

Pattern scope describes whether a pattern reuses behavior through class inheritance (class scope) or object composition (object scope). The Gang of Four preferred object composition because it lets relationships change at runtime, while inheritance binds them at the type level. Most Gang of Four patterns use composition.

Learn more on Codecademy

  • Learn how to apply SOLID principles, design patterns, and layered architecture with AI to design and refactor full-stack applications.
    • With Certificate
    • Intermediate.
      4 hours