OOP Design Patterns C# Architecture

Design Patterns in OOP: A Practical Introduction

June 03, 2026

What Are Design Patterns?

Design patterns are reusable solutions to commonly occurring problems in software design. They were popularised by the "Gang of Four" book and are grouped into three categories.

Creational Patterns

These deal with object creation mechanisms.

Singleton

Ensures a class has only one instance and provides a global access point.

public sealed class AppConfig {
    private static AppConfig? _instance;
    private static readonly object _lock = new();

    private AppConfig() { }

    public static AppConfig Instance {
        get {
            lock (_lock) {
                return _instance ??= new AppConfig();
            }
        }
    }
}

Factory Method

Defines an interface for creating objects, letting subclasses decide which class to instantiate.

abstract class Notification {
    public abstract void Send(string message);
}
class EmailNotification : Notification {
    public override void Send(string msg) => Console.WriteLine($"Email: {msg}");
}
class SmsNotification : Notification {
    public override void Send(string msg) => Console.WriteLine($"SMS: {msg}");
}
class NotificationFactory {
    public static Notification Create(string type) => type switch {
        "email" => new EmailNotification(),
        "sms" => new SmsNotification(),
        _ => throw new ArgumentException("Unknown type")
    };
}

Structural Patterns

Decorator

Adds behaviour to objects dynamically without altering their class.

interface ILogger { void Log(string msg); }
class ConsoleLogger : ILogger {
    public void Log(string msg) => Console.WriteLine(msg);
}
class TimestampLogger : ILogger {
    private readonly ILogger _inner;
    public TimestampLogger(ILogger inner) { _inner = inner; }
    public void Log(string msg) => _inner.Log($"[{DateTime.Now}] {msg}");
}

Behavioural Patterns

Observer

Defines a one-to-many dependency so when one object changes state, all dependents are notified.

class EventPublisher {
    private readonly List<Action<string>> _subscribers = new();
    public void Subscribe(Action<string> handler) => _subscribers.Add(handler);
    public void Publish(string evt) => _subscribers.ForEach(h => h(evt));
}

Conclusion

Design patterns are a shared vocabulary for developers. Knowing when and how to apply them is a hallmark of an experienced software engineer.