SOLID & Design Patterns

SOLID Principles & Design Patterns Quick Reference


SOLID Principles Overview

SOLID = Five principles for object-oriented design

Purpose:

  • Maintainable code

  • Testable code

  • Flexible and extensible

  • Reduce coupling

  • Easier to understand

Principle
Description

S - Single Responsibility

A class should have one reason to change

O - Open/Closed

Open for extension, closed for modification

L - Liskov Substitution

Subtypes must be substitutable for base types

I - Interface Segregation

Many specific interfaces > one general interface

D - Dependency Inversion

Depend on abstractions, not concretions


Single Responsibility Principle (SRP)

Definition: A class should have only one reason to change (one responsibility)

❌ Violation

✅ Following SRP


Open/Closed Principle (OCP)

Definition: Software entities should be open for extension but closed for modification

❌ Violation

✅ Following OCP

Real-World Example: Discount Calculator


Liskov Substitution Principle (LSP)

Definition: Objects of a superclass should be replaceable with objects of subclasses without breaking the application

❌ Violation

✅ Following LSP

Real-World Example: Bird Hierarchy


Interface Segregation Principle (ISP)

Definition: Clients should not be forced to depend on interfaces they don't use

❌ Violation

✅ Following ISP

Real-World Example: Printer Interfaces


Dependency Inversion Principle (DIP)

Definition: High-level modules should not depend on low-level modules. Both should depend on abstractions.

❌ Violation

✅ Following DIP

Real-World Example: Data Access


Design Patterns

Creational Patterns

Design patterns for object creation mechanisms


Singleton Pattern

Purpose: Ensure a class has only one instance and provide global access to it

When to use:

  • ✅ Need exactly one instance (configuration, logging, cache)

  • ✅ Global access point required

  • ✅ Lazy initialization needed

When NOT to use:

  • ❌ Testing is difficult (global state)

  • ❌ Can cause tight coupling

  • ❌ Not thread-safe by default

Implementation

Real-World Example: Logger


Factory Pattern

Purpose: Create objects without specifying exact class

When to use:

  • ✅ Object creation logic is complex

  • ✅ Need to create different types based on input

  • ✅ Want to hide creation logic from client

Simple Factory

Factory Method


Abstract Factory Pattern

Purpose: Create families of related objects without specifying concrete classes

When to use:

  • ✅ Need to create families of related objects

  • ✅ Want to ensure objects from the same family are used together

Implementation


Builder Pattern

Purpose: Construct complex objects step by step

When to use:

  • ✅ Object has many optional parameters

  • ✅ Construction process is complex

  • ✅ Want to create different representations

Implementation

Fluent Builder with Director


Prototype Pattern

Purpose: Create new objects by copying existing ones

When to use:

  • ✅ Object creation is expensive

  • ✅ Need to create many similar objects

  • ✅ Want to avoid subclasses

Implementation


Structural Patterns

Design patterns for composing classes and objects


Adapter Pattern

Purpose: Convert interface of a class into another interface clients expect

When to use:

  • ✅ Want to use existing class with incompatible interface

  • ✅ Need to integrate third-party libraries

  • ✅ Legacy code integration

Implementation


Decorator Pattern

Purpose: Add new functionality to existing objects dynamically

When to use:

  • ✅ Need to add responsibilities dynamically

  • ✅ Want to avoid subclass explosion

  • ✅ Extension by composition rather than inheritance

Implementation


Facade Pattern

Purpose: Provide simplified interface to complex subsystem

When to use:

  • ✅ Complex subsystem with many classes

  • ✅ Want to hide complexity from clients

  • ✅ Need layer between client and subsystem

Implementation


Proxy Pattern

Purpose: Provide placeholder or surrogate for another object

When to use:

  • ✅ Control access to object (protection proxy)

  • ✅ Lazy initialization (virtual proxy)

  • ✅ Remote objects (remote proxy)

  • ✅ Caching (cache proxy)

Virtual Proxy (Lazy Loading)

Protection Proxy


Behavioral Patterns

Design patterns for communication between objects


Strategy Pattern

Purpose: Define family of algorithms and make them interchangeable

When to use:

  • ✅ Multiple algorithms for specific task

  • ✅ Want to avoid conditional statements

  • ✅ Need to switch algorithms at runtime

Implementation

Real-World: Payment Processing


Observer Pattern

Purpose: Define one-to-many dependency so when one object changes state, all dependents are notified

When to use:

  • ✅ Object state change affects other objects

  • ✅ Don't know how many objects need notification

  • ✅ Event-driven systems

Implementation

Using C# Events


Command Pattern

Purpose: Encapsulate request as object, allowing parameterization and queuing

When to use:

  • ✅ Need to queue operations

  • ✅ Support undo/redo

  • ✅ Log operations

  • ✅ Decouple sender and receiver

Implementation


Template Method Pattern

Purpose: Define skeleton of algorithm, letting subclasses override specific steps

When to use:

  • ✅ Common algorithm structure with varying steps

  • ✅ Want to avoid code duplication

  • ✅ Control extension points

Implementation


Quick Reference: When to Use Which Pattern

Creational Patterns

Pattern
Use When

Singleton

Need exactly one instance (logger, config)

Factory

Create objects based on runtime conditions

Abstract Factory

Need families of related objects

Builder

Object has many optional parameters

Prototype

Object creation is expensive, need copies

Structural Patterns

Pattern
Use When

Adapter

Integrate incompatible interfaces

Decorator

Add responsibilities dynamically

Facade

Simplify complex subsystem

Proxy

Control access or add lazy loading

Behavioral Patterns

Pattern
Use When

Strategy

Multiple algorithms, choose at runtime

Observer

Object changes affect multiple objects

Command

Queue operations, support undo

Template Method

Algorithm structure with varying steps


Guide Complete! This comprehensive guide covers all SOLID principles with C# examples, and the most important design patterns (Creational, Structural, and Behavioral). Master these to write maintainable, flexible, and testable code! 🎯

Last updated