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.