OOP Quick Reference

Object-Oriented Programming

Design Pattern Categories

Creational Patterns (5 patterns)

Focus on object creation mechanisms, increasing flexibility and reuse.

Pattern Purpose When to Use When to Avoid
Factory Method Define interface for creating objects, let subclasses decide which class to instantiate Multiple creation paths, hide instantiation complexity Single, simple constructor suffices
Abstract Factory Create families of related objects without specifying concrete classes Need consistent families of objects (UI themes, cross-platform) Only one product family exists
Builder Construct complex objects step-by-step Objects with many optional parameters, immutable objects Simple objects with few properties
Prototype Create new objects by cloning existing ones Expensive object creation, object state templates Objects are cheap to create from scratch
Singleton Ensure only one instance exists globally Shared resources (logging, config) - Use DI instead Almost always - prefer dependency injection

Structural Patterns (7 patterns)

Deal with object composition and relationships.

Pattern Purpose When to Use When to Avoid
Adapter Make incompatible interfaces work together Integrating legacy code, third-party libraries You control both interfaces - fix the design
Bridge Separate abstraction from implementation Avoid Cartesian product explosion (N abstractions × M implementations) Simple one-to-one relationship
Composite Treat individual objects and compositions uniformly Tree structures (files/folders, UI components) Flat structure with no hierarchy
Decorator Add behavior to objects dynamically Flexible alternative to subclassing, runtime behavior modification Compile-time behavior definition is sufficient
Facade Provide simplified interface to complex subsystems Hide complexity, reduce dependencies on subsystem Subsystem is already simple
Flyweight Share common state across many objects to reduce memory Large number of similar objects, memory constraints Few objects or unique state dominates
Proxy Control access to objects (lazy loading, access control, caching) Expensive object creation, access control, caching Direct access is simpler and sufficient

Behavioral Patterns (11 patterns)

Focus on communication between objects and responsibility distribution.

Pattern Purpose When to Use When to Avoid
Chain of Responsibility Pass requests along a chain of handlers Multiple handlers, handler selection at runtime Single handler or static handler selection
Command Encapsulate requests as objects Undo/redo, queuing operations, logging requests Simple method calls suffice
Interpreter Define grammar and interpreter for a language Simple domain-specific languages Complex grammars - use parser generator
Iterator Access collection elements sequentially without exposing structure Custom traversal algorithms Standard foreach loop works
Mediator Centralize complex communications between objects Many-to-many object relationships Simple one-to-one relationships
Memento Capture and restore object state Undo/redo functionality, snapshots State is simple enough to store directly
Observer Notify dependents of state changes Event-driven systems, publish-subscribe Simple callbacks or direct calls work
State Change behavior based on internal state Complex state-dependent behavior Simple if/switch statements suffice
Strategy Define family of algorithms, make them interchangeable Runtime algorithm selection, multiple implementations Single algorithm, no variation needed
Template Method Define algorithm skeleton, let subclasses override steps Common algorithm structure with varying steps No variation in algorithm steps
Visitor Add operations to object hierarchies without modifying them Operations on complex object structures Simple structures - use pattern matching

Pattern Selection Decision Tree

“I need to create objects…”

Do you have multiple ways to create the object?
├─ YES: Complex construction process?
│   ├─ YES: Many optional parameters → Builder
│   └─ NO: Multiple factory methods → Factory Method
└─ NO: Need consistent families of objects?
    ├─ YES → Abstract Factory
    ├─ NO: Clone existing objects → Prototype
    └─ NO: Single instance needed → Singleton (prefer DI)

“I need to structure relationships…”

Working with incompatible interfaces?
├─ YES: Wrapping legacy/third-party → Adapter
└─ NO: Need to hide complexity?
    ├─ YES → Facade
    └─ NO: Tree structure?
        ├─ YES → Composite
        └─ NO: Add behavior dynamically?
            ├─ YES → Decorator
            └─ NO: Control object access → Proxy

“I need to manage behavior…”

Multiple handlers for a request?
├─ YES: Chain of handlers → Chain of Responsibility
└─ NO: Algorithm varies at runtime?
    ├─ YES → Strategy
    └─ NO: Need undo/redo?
        ├─ YES: For operations → Command
        └─ YES: For state → Memento
        └─ NO: Notify multiple objects?
            ├─ YES → Observer
            └─ NO: State-dependent behavior → State

Common Anti-Patterns to Avoid

God Object

Problem: One class does everything.

// BAD
public class Application
{
    public void ProcessUserLogin() { }
    public void GenerateReports() { }
    public void SendEmails() { }
    public void ManageDatabase() { }
    public void HandlePayments() { }
}

// GOOD: Single Responsibility Principle
public class AuthenticationService { }
public class ReportGenerator { }
public class EmailService { }
public class DataRepository { }
public class PaymentProcessor { }

Spaghetti Code

Problem: Tangled dependencies, difficult to follow.

// BAD: Tight coupling
public class OrderService
{
    public void Process(Order order)
    {
        var validator = new OrderValidator();
        var calculator = new PriceCalculator();
        var repository = new OrderRepository();
        // Direct dependencies everywhere
    }
}

// GOOD: Dependency Injection
public class OrderService
{
    private readonly IOrderValidator validator;
    private readonly IPriceCalculator calculator;
    private readonly IOrderRepository repository;

    public OrderService(IOrderValidator validator, IPriceCalculator calculator, IOrderRepository repository)
    {
        this.validator = validator;
        this.calculator = calculator;
        this.repository = repository;
    }
}

Tight Coupling

Problem: Changes in one class force changes in many others.

// BAD
public class EmailSender
{
    public void Send(string to, string message)
    {
        var smtp = new SmtpClient("smtp.gmail.com"); // Tight coupling
        smtp.Send(to, message);
    }
}

// GOOD: Program to interfaces
public interface IEmailSender
{
    void Send(string to, string message);
}

public class SmtpEmailSender : IEmailSender
{
    private readonly string smtpServer;

    public SmtpEmailSender(string smtpServer)
    {
        this.smtpServer = smtpServer;
    }

    public void Send(string to, string message)
    {
        var smtp = new SmtpClient(smtpServer);
        smtp.Send(to, message);
    }
}

Premature Optimization

Problem: Complex patterns without justification.

// BAD: Over-engineered for simple task
public interface INumberAdder { }
public class NumberAdderFactory { }
public class NumberAdderContext { }
public class NumberAdderStrategy { }

// GOOD: Keep it simple
public class Calculator
{
    public int Add(int a, int b) => a + b;
}

Pattern Overuse

Problem: Using patterns where simple code suffices.

// BAD: Unnecessary abstraction
public interface IGreeter { string Greet(); }
public class HelloGreeter : IGreeter { public string Greet() => "Hello"; }
public class GreeterFactory { public IGreeter Create() => new HelloGreeter(); }

// GOOD: Direct implementation
public string Greet() => "Hello";

Modern Framework Integration

ASP.NET Core Patterns

Framework Feature Design Pattern Example
Dependency Injection Service Locator, Factory services.AddScoped<IUserService, UserService>()
Middleware Pipeline Chain of Responsibility app.UseAuthentication().UseAuthorization()
Options Pattern Builder services.Configure<MyOptions>(config)
Filters Decorator [Authorize], [ValidateModel]
Tag Helpers Decorator/Adapter <asp-validation-for>
Model Binding Adapter Request → Model conversion

Entity Framework Core Patterns

Framework Feature Design Pattern Example
DbContext Unit of Work Transaction management
DbSet Repository context.Users.Where(...)
Change Tracker Observer Track entity state changes
Lazy Loading Proxy Virtual navigation properties
Migration Command Add-Migration, Update-Database
Value Converters Adapter Custom type conversions

SignalR Patterns

Framework Feature Design Pattern Example
Hub Facade Simple interface to complex messaging
Hub Clients Observer Broadcast to connected clients
Groups Mediator Manage client groups

MediatR (CQRS Library)

Framework Feature Design Pattern Example
Request/Response Command/Query Send(new GetUserQuery())
Handlers Chain of Responsibility Pipeline behaviors
Notifications Observer/Mediator Publish(new UserCreatedEvent())

SOLID Principles Quick Reference

Principle Key Question Red Flag Solution
Single Responsibility “Why would this class change?” Class has multiple reasons to change Extract classes for each responsibility
Open-Closed “Can I extend without modifying?” Long if/switch on type Use polymorphism via interfaces
Liskov Substitution “Can I substitute derived for base?” Exceptions thrown in overrides Redesign hierarchy or use interfaces
Interface Segregation “Do I use all interface methods?” NotImplementedException Split into smaller interfaces
Dependency Inversion “Do I depend on abstractions?” new keyword everywhere Inject dependencies via constructor

When to Use OOP vs Other Paradigms

Object-Oriented Programming

Best For:

  • Business domain modeling
  • Complex state management
  • Clear entity relationships
  • Polymorphic behavior
  • Framework/library design

Example: E-commerce system with Products, Orders, Customers

Functional Programming

Best For:

  • Data transformation pipelines
  • Stateless operations
  • Mathematical computations
  • Concurrent operations
  • Immutable data

Example: Data processing, LINQ queries

Procedural Programming

Best For:

  • Simple scripts
  • Linear workflows
  • System utilities
  • One-off tasks

Example: Build scripts, data migration

Hybrid Approach (Modern C#)

// OOP for domain entities
public class Order
{
    public int Id { get; init; }
    public List<OrderItem> Items { get; init; }
    public decimal Total => Items.Sum(i => i.Price);
}

// Functional for transformations
public static class OrderExtensions
{
    public static IEnumerable<Order> FilterByDate(this IEnumerable<Order> orders, DateTime start, DateTime end) =>
        orders.Where(o => o.CreatedDate >= start && o.CreatedDate <= end);

    public static decimal CalculateTotalRevenue(this IEnumerable<Order> orders) =>
        orders.Sum(o => o.Total);
}

// Usage: Combines both approaches
var revenue = orders
    .FilterByDate(startDate, endDate)
    .CalculateTotalRevenue();

Pattern Complexity Guide

Beginner-Friendly Patterns

Start with these - easy to understand and apply:

  • Factory Method: Simple object creation
  • Singleton: Single instance (use DI)
  • Strategy: Interchangeable algorithms
  • Observer: Event handling (use C# events)
  • Decorator: Add behavior dynamically
  • Facade: Simplify complex systems

Intermediate Patterns

Learn after mastering basics:

  • Builder: Complex object construction
  • Adapter: Interface compatibility
  • Command: Undo/redo operations
  • State: State-dependent behavior
  • Template Method: Algorithm skeleton
  • Proxy: Control access

Advanced Patterns

Require deeper understanding:

  • Abstract Factory: Object family creation
  • Bridge: Abstraction/implementation separation
  • Composite: Tree structures
  • Chain of Responsibility: Request handling chain
  • Visitor: Operations on hierarchies
  • Flyweight: Memory optimization
  • Interpreter: Language processing
  • Mediator: Complex object interactions
  • Memento: State snapshots

Remember: Patterns are tools, not goals. Use them to solve specific problems, not because they exist. Modern development emphasizes simplicity, testability, and maintainability over pattern implementation. When in doubt, choose simple code over clever patterns.


Found this guide helpful? Share it with your team:

Share on LinkedIn