C# Design Patterns: Essential Reference for Developers
C# Design Patterns: Essential Reference
Creational Design Patterns
Pattern | When to Use | Key Implementation |
---|---|---|
Singleton | Need exactly one instance globally accessible. | private static Singleton _instance; public static Singleton Instance => _instance ??= new Singleton(); |
Factory | Create objects without specifying concrete classes. | public static IProduct Create(string type) => type switch { "A" => new ProductA(), ... } |
Builder | Construct complex objects step-by-step. | public class CarBuilder { ... public CarBuilder WithEngine(...) { ... } } |
Prototype | Clone existing objects instead of creating new ones. | public interface IPrototype { IPrototype Clone(); } |
Structural Design Patterns
Pattern | When to Use | Key Implementation |
---|---|---|
Adapter | Make incompatible interfaces work together. | public class NewSystemAdapter : ILegacyInterface { private NewSystem _newSystem; } |
Decorator | Add functionality to objects dynamically. | public abstract class CoffeeDecorator : ICoffee { protected ICoffee _decoratedCoffee; } |
Facade | Simplify complex systems with a unified interface. | public class OrderFacade { public void PlaceOrder() { /* calls 5+ subsystems */ } } |
Proxy | Control access to another object. | public class Proxy : IService { private RealService _service; public void Request() { /* access control */ } } |
Behavioral Design Patterns
Pattern | When to Use | Key Implementation |
---|---|---|
Observer | Notify multiple objects about state changes. | public class Subject { private List<IObserver> _observers = new(); public void Notify() { ... } } |
Strategy | Switch algorithms at runtime. | public class Context { private IStrategy _strategy; public void SetStrategy(IStrategy s) { ... } } |
Command | Encapsulate requests as objects. | public interface ICommand { void Execute(); } public class OrderCommand : ICommand { ... } |
State | Change object behavior when its state changes. | public class Context { private IState _state; public void Request() { _state.Handle(); } } |
Key Software Design Principles
Encapsulate What Varies
Separate changing parts from stable parts.Program to Interfaces, Not Implementations
List<IShape> shapes = new();
notList<Circle> circles = new();
Favor Composition Over Inheritance
Car _engine = new V8Engine();
vsclass Car : Engine
SOLID Principles:
Single Responsibility Principle
Open/Closed Principle
Liskov Substitution Principle
Interface Segregation Principle
Dependency Inversion Principle
Common Anti-Patterns to Avoid
God Object: A single class handling too many responsibilities.
Spaghetti Code: Lack of clear structure, with tangled dependencies.
Reinvent the Wheel: Developing custom solutions for already solved problems.
Pattern Overuse: Applying complex patterns when simpler code is sufficient.
Top 5 Essential C# Design Patterns
1. Singleton Pattern (Global Access)
public class Logger { private static Logger _instance; private static readonly object _lock = new(); private Logger() {} public static Logger Instance { get { lock (_lock) { return _instance ??= new Logger(); } } } }
2. Factory Pattern (Creation Delegation)
public interface IAnimal { void Speak(); } public class Dog : IAnimal { public void Speak() => Console.WriteLine("Woof!"); } public static class AnimalFactory { public static IAnimal Create(string type) => type switch { "dog" => new Dog(), "cat" => new Cat(), _ => throw new ArgumentException("Invalid animal type") }; }
3. Observer Pattern (Event Notification)
public class NewsPublisher { private List<ISubscriber> _subscribers = new(); public void Subscribe(ISubscriber s) => _subscribers.Add(s); public void Notify(string news) => _subscribers.ForEach(s => s.Update(news)); } public class Subscriber : ISubscriber { public void Update(string news) => Console.WriteLine($"Received: {news}"); }
4. Strategy Pattern (Algorithm Switching)
public interface IPaymentStrategy { void Pay(double amount); } public class CreditCardPayment : IPaymentStrategy { public void Pay(double amount) => Console.WriteLine($"Paid ${amount} via Credit Card"); } public class ShoppingCart { private IPaymentStrategy _payment; public void SetPayment(IPaymentStrategy payment) => _payment = payment; public void Checkout(double total) => _payment.Pay(total); }
5. Decorator Pattern (Dynamic Extensions)
public interface ICoffee { double Cost(); } public class BasicCoffee : ICoffee { public double Cost() => 2.50; } public class MilkDecorator : ICoffee { private readonly ICoffee _coffee; public MilkDecorator(ICoffee coffee) => _coffee = coffee; public double Cost() => _coffee.Cost() + 0.50; } <em>// Usage:</em> ICoffee coffee = new MilkDecorator(new BasicCoffee()); Console.WriteLine(coffee.Cost()); <em>// Output: 3.00</em>
Important Reminders:
🛠️ Patterns are tools, not goals.
🔧 Use them when they solve specific problems.
⚖️ Simplicity > Pattern Correctness.
📚 Reference: <em>Design Patterns: Elements of Reusable Object-Oriented Software</em> (Gang of Four)