techmore.in

C# - Design Patterns

Design patterns are reusable solutions to common problems in software design. In C#, design patterns can help structure your code in a way that is modular, maintainable, and scalable. Here are some commonly used design patterns in C#, categorized into creational, structural, and behavioral patterns:

1. Creational Patterns

Creational patterns deal with object creation mechanisms, aiming to create objects in a manner suitable to the situation.

Singleton Pattern

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

csharp
public class Singleton { private static Singleton _instance; private static readonly object _lock = new object(); private Singleton() { } public static Singleton Instance { get { lock (_lock) { if (_instance == null) { _instance = new Singleton(); } return _instance; } } } }

Factory Method Pattern

Defines an interface for creating objects, but allows subclasses to alter the type of objects that will be created.

csharp
public abstract class Product { public abstract string GetName(); } public class ConcreteProductA : Product { public override string GetName() => "ConcreteProductA"; } public class ConcreteProductB : Product { public override string GetName() => "ConcreteProductB"; } public abstract class Creator { public abstract Product FactoryMethod(); } public class ConcreteCreatorA : Creator { public override Product FactoryMethod() => new ConcreteProductA(); } public class ConcreteCreatorB : Creator { public override Product FactoryMethod() => new ConcreteProductB(); }

Abstract Factory Pattern

Provides an interface for creating families of related or dependent objects without specifying their concrete classes.

csharp
public interface IProductA { string GetProductA(); } public interface IProductB { string GetProductB(); } public class ProductA1 : IProductA { public string GetProductA() => "ProductA1"; } public class ProductB1 : IProductB { public string GetProductB() => "ProductB1"; } public class ProductA2 : IProductA { public string GetProductA() => "ProductA2"; } public class ProductB2 : IProductB { public string GetProductB() => "ProductB2"; } public interface IAbstractFactory { IProductA CreateProductA(); IProductB CreateProductB(); } public class ConcreteFactory1 : IAbstractFactory { public IProductA CreateProductA() => new ProductA1(); public IProductB CreateProductB() => new ProductB1(); } public class ConcreteFactory2 : IAbstractFactory { public IProductA CreateProductA() => new ProductA2(); public IProductB CreateProductB() => new ProductB2(); }

2. Structural Patterns

Structural patterns deal with object composition, creating relationships between objects to form larger structures.

Adapter Pattern

Allows incompatible interfaces to work together by converting the interface of a class into another interface clients expect.

csharp
public interface ITarget { string GetRequest(); } public class Adaptee { public string SpecificRequest() => "Specific Request"; } public class Adapter : ITarget { private readonly Adaptee _adaptee; public Adapter(Adaptee adaptee) { _adaptee = adaptee; } public string GetRequest() => _adaptee.SpecificRequest(); }

Decorator Pattern

Adds new functionality to an object dynamically without altering its structure.

csharp
public interface IComponent { string Operation(); } public class ConcreteComponent : IComponent { public string Operation() => "ConcreteComponent"; } public abstract class Decorator : IComponent { protected IComponent _component; protected Decorator(IComponent component) { _component = component; } public virtual string Operation() => _component.Operation(); } public class ConcreteDecoratorA : Decorator { public ConcreteDecoratorA(IComponent component) : base(component) { } public override string Operation() => $"ConcreteDecoratorA({_component.Operation()})"; } public class ConcreteDecoratorB : Decorator { public ConcreteDecoratorB(IComponent component) : base(component) { } public override string Operation() => $"ConcreteDecoratorB({_component.Operation()})"; }

Facade Pattern

Provides a simplified interface to a complex subsystem, making it easier to use.

csharp
public class SubsystemA { public void OperationA() => Console.WriteLine("SubsystemA Operation"); } public class SubsystemB { public void OperationB() => Console.WriteLine("SubsystemB Operation"); } public class Facade { private readonly SubsystemA _subsystemA; private readonly SubsystemB _subsystemB; public Facade() { _subsystemA = new SubsystemA(); _subsystemB = new SubsystemB(); } public void Operation() { _subsystemA.OperationA(); _subsystemB.OperationB(); } }

3. Behavioral Patterns

Behavioral patterns deal with how objects interact and how responsibilities are distributed.

Observer Pattern

Defines a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.

csharp
public interface IObserver { void Update(string message); } public interface ISubject { void Attach(IObserver observer); void Detach(IObserver observer); void Notify(string message); } public class ConcreteSubject : ISubject { private readonly List<IObserver> _observers = new List<IObserver>(); public void Attach(IObserver observer) => _observers.Add(observer); public void Detach(IObserver observer) => _observers.Remove(observer); public void Notify(string message) { foreach (var observer in _observers) { observer.Update(message); } } } public class ConcreteObserver : IObserver { private readonly string _name; public ConcreteObserver(string name) { _name = name; } public void Update(string message) => Console.WriteLine($"{_name} received: {message}"); }

Strategy Pattern

Defines a family of algorithms, encapsulates each one, and makes them interchangeable. Strategy lets the algorithm vary independently from clients that use it.

csharp
public interface IStrategy { void Execute(); } public class ConcreteStrategyA : IStrategy { public void Execute() => Console.WriteLine("Strategy A"); } public class ConcreteStrategyB : IStrategy { public void Execute() => Console.WriteLine("Strategy B"); } public class Context { private readonly IStrategy _strategy; public Context(IStrategy strategy) { _strategy = strategy; } public void PerformOperation() => _strategy.Execute(); }

Command Pattern

Encapsulates a request as an object, thereby allowing for parameterization of clients with queues, requests, and operations.

csharp
public interface ICommand { void Execute(); } public class Receiver { public void Action() => Console.WriteLine("Receiver Action"); } public class ConcreteCommand : ICommand { private readonly Receiver _receiver; public ConcreteCommand(Receiver receiver) { _receiver = receiver; } public void Execute() => _receiver.Action(); } public class Invoker { private ICommand _command; public void SetCommand(ICommand command) => _command = command; public void Invoke() => _command.Execute(); }

These patterns are foundational concepts in software design that help manage object creation, composition, and interaction. They promote code reuse, flexibility, and maintainability, and are widely used in both small and large-scale software projects.