Design Patterns in C# with Examples 2024
Design Patterns in C# with Examples : Design patterns are crucial tools in software engineering, offering general solutions to common design problems. They streamline development, improve code readability, and make systems more flexible and maintainable. This blog post explores various design patterns in C# with examples, providing a solid foundation for applying these patterns in your projects. Let’s dive in!
Table of Contents
Introduction of Design Patterns
Design patterns are standard solutions to frequent problems in software design. Each pattern is as a blueprint that can be customized to solve a particular design problem in your code. Design patterns fall into three main categories:
Learn something interested about boxing and unboxing in c# with example.
- Creational Patterns: Deal with object creation mechanisms.
- Structural Patterns: Concerned with object composition or relationships.
- Behavioral Patterns: Focus on object interaction and responsibility.
Creational Design Patterns
Singleton design pattern
The Singleton design pattern in c# is a fundamental concept in software engineering, offering a structured approach to ensure that a class has only one instance across the application. This pattern is specially useful in scenarios where there’s a need for a single, global point of access to a class instance, like managing configuration settings or want to implement a logging system. By enforcing a single instance, the Singleton pattern helps streamline resource management and promotes consistency across the application. Understanding the Singleton design pattern is necessary for developers looking to create efficient and maintainable software solutions, as it provides a robust foundation for managing object instances effectively.
public class Singleton
{
private static Singleton _instance;
private Singleton() { }
public static Singleton Instance
{
get
{
if (_instance == null)
{
_instance = new Singleton();
}
return _instance;
}
}
}
Factory Method design pattern
The Factory Method design pattern in c# is a powerful design pattern in software development that provides a structured approach for creating objects. This pattern defines an interface for creating objects, but allows subclasses to modify the type of objects that will be instantiated. By encapsulating object creation logic within subclasses, the Factory Method pattern promotes flexibility and extensibility in the codebase. Developers frequently utilize this pattern in scenarios where the specific subclass of an object to be created is determined at runtime. Understanding and implementing the Factory Method design pattern is crucial for constructing scalable and maintainable software architectures, as it enables the creation of objects in a consistent and organized manner.
public abstract class Product { }
public class ConcreteProduct : Product { }
public abstract class Creator
{
public abstract Product FactoryMethod();
}
public class ConcreteCreator : Creator
{
public override Product FactoryMethod()
{
return new ConcreteProduct();
}
}
Abstract Factory design pattern
The Abstract Factory design pattern in c#, a cornerstone of software development, offers a structured method for creating families of related or dependent objects without the need to specify their concrete classes. This pattern encapsulates a group of individual factories that share a common theme, allowing clients to create objects without being aware of their underlying implementations. By promoting loose coupling between client code and concrete classes, the Abstract Factory pattern enhances flexibility and maintainability in software architectures. Developers often apply this pattern in scenarios where multiple families of related objects need to be created and used interchangeably. Understanding and implementing the Abstract Factory design patterns c# with example is crucial for creating modular and extensible software systems that may adapt to changing requirements with ease.
Lets see below Abstract Factory design patterns c# with example
public interface IAbstractFactory
{
IProductA CreateProductA();
IProductB CreateProductB();
}
public class ConcreteFactory1 : IAbstractFactory
{
public IProductA CreateProductA() => new ProductA1();
public IProductB CreateProductB() => new ProductB1();
}
Builder design pattern
The Builder design pattern is a vital concept in software development, providing a systematic approach to constructing complex objects. Unlike other creational patterns, the Builder design pattern separates the construction of an object from its representation, allowing the same construction process to create different representations. This pattern is especially useful when an object requires numerous steps to be assembled or when the construction process must be controlled and simplified. By using the Builder design pattern, developers can create objects in a step-by-step manner, ensuring that the final product is assembled correctly and efficiently. Understanding and implementing the Builder design pattern is crucial for creating flexible and maintainable code, making it easier to manage the construction of complex objects in software projects.
public class Product
{
public List<string> Parts = new List<string>();
}
public abstract class Builder
{
public abstract void BuildPartA();
public abstract void BuildPartB();
public abstract Product GetResult();
}
public class ConcreteBuilder : Builder
{
private Product _product = new Product();
public override void BuildPartA() => _product.Parts.Add("PartA");
public override void BuildPartB() => _product.Parts.Add("PartB");
public override Product GetResult() => _product;
}
Prototype design pattern
The Prototype design pattern is a key concept in software development that focuses on creating new objects by copying existing ones, known as prototypes. This pattern is particularly useful when the cost of creating a new instance of a class is high or when instances of the class have only a few different states. By using the Prototype design pattern, developers can clone objects efficiently, preserving the state of the original object and reducing the overhead of initialization. This approach enhances performance and provides flexibility in managing object creation, especially in systems that require frequent object creation and customization. Understanding and implementing the Prototype design pattern is essential for developers aiming to optimize resource usage and improve the efficiency of their software applications.
The Prototype pattern allows an object to create a clone of itself.
Structural Design Patterns
Adapter design pattern
The Adapter pattern facilitates the collaboration of incompatible interfaces.
Lets see below c# design patterns with examples of Adapter design pattern
public interface ITarget
{
void Request();
}
public class Adaptee
{
public void SpecificRequest()
{
Console.WriteLine("Called SpecificRequest()");
}
}
public class Adapter : ITarget
{
private Adaptee _adaptee = new Adaptee();
public void Request()
{
_adaptee.SpecificRequest();
}
}
Bridge design pattern
The Bridge pattern decouples an object’s abstraction from its implementation, enabling them to evolve independently of each other.
Learn about Collection in c#.
public interface IImplementor
{
void OperationImpl();
}
public class ConcreteImplementorA : IImplementor
{
public void OperationImpl()
{
Console.WriteLine("ConcreteImplementorA Operation");
}
}
public abstract class Abstraction
{
protected IImplementor implementor;
public Abstraction(IImplementor implementor)
{
this.implementor = implementor;
}
public abstract void Operation();
}
public class RefinedAbstraction : Abstraction
{
public RefinedAbstraction(IImplementor implementor) : base(implementor) { }
public override void Operation()
{
implementor.OperationImpl();
}
}
Composite design pattern
The Composite pattern enables the composition of objects into tree structures, representing part-whole hierarchies.
Lets see below c# design patterns with examples of Composite design pattern
public interface IComponent
{
void Operation();
}
public class Leaf : IComponent
{
public void Operation()
{
Console.WriteLine("Leaf Operation");
}
}
public class Composite : IComponent
{
private List<IComponent> _children = new List<IComponent>();
public void Add(IComponent component)
{
_children.Add(component);
}
public void Operation()
{
foreach (var child in _children)
{
child.Operation();
}
}
}
Decorator design pattern
The Decorator pattern permits the dynamic addition of behavior to individual objects.
public abstract class Component
{
public abstract void Operation();
}
public class ConcreteComponent : Component
{
public override void Operation()
{
Console.WriteLine("ConcreteComponent Operation");
}
}
public abstract class Decorator : Component
{
protected Component _component;
public Decorator(Component component)
{
_component = component;
}
public override void Operation()
{
_component.Operation();
}
}
public class ConcreteDecorator : Decorator
{
public ConcreteDecorator(Component component) : base(component) { }
public override void Operation()
{
base.Operation();
Console.WriteLine("ConcreteDecorator Operation");
}
}
Facade design pattern
The Facade pattern offers a simplified interface to a complex subsystem.
public class SubsystemA
{
public void OperationA()
{
Console.WriteLine("SubsystemA Operation");
}
}
public class SubsystemB
{
public void OperationB()
{
Console.WriteLine("SubsystemB Operation");
}
}
public class Facade
{
private SubsystemA _subsystemA;
private SubsystemB _subsystemB;
public Facade()
{
_subsystemA = new SubsystemA();
_subsystemB = new SubsystemB();
}
public void Operation()
{
_subsystemA.OperationA();
_subsystemB.OperationB();
}
}
Flyweight design pattern
The Flyweight pattern minimizes memory usage by sharing as much data as possible with similar objects.
public class Flyweight
{
private string _intrinsicState;
public Flyweight(string intrinsicState)
{
_intrinsicState = intrinsicState;
}
public void Operation(string extrinsicState)
{
Console.WriteLine($"Intrinsic: {_intrinsicState}, Extrinsic: {extrinsicState}");
}
}
public class FlyweightFactory
{
private Dictionary<string, Flyweight> _flyweights = new Dictionary<string, Flyweight>();
public Flyweight GetFlyweight(string key)
{
if (!_flyweights.ContainsKey(key))
{
_flyweights[key] = new Flyweight(key);
}
return _flyweights[key];
}
}
Proxy design pattern
The Proxy pattern furnishes a surrogate or placeholder for another object to manage its access.
public interface ISubject
{
void Request();
}
public class RealSubject : ISubject
{
public void Request()
{
Console.WriteLine("RealSubject Request");
}
}
public class Proxy : ISubject
{
private RealSubject _realSubject;
public void Request()
{
if (_realSubject == null)
{
_realSubject = new RealSubject();
}
_realSubject.Request();
}
}
Behavioral Design Patterns
Chain of Responsibility design pattern
The Chain of Responsibility pattern forwards a request through a chain of handlers.
public abstract class Handler
{
protected Handler successor;
public void SetSuccessor(Handler successor)
{
this.successor = successor;
}
public abstract void HandleRequest(int request);
}
public class ConcreteHandler1 : Handler
{
public override void HandleRequest(int request)
{
if (request == 1)
{
Console.WriteLine("Handled by ConcreteHandler1");
}
else if (successor != null)
{
successor.HandleRequest(request);
}
}
}
Command design pattern
The Command pattern turns a request into a stand-alone object that contains all information about the request.
public interface ICommand
{
void Execute();
}
public class Receiver
{
public void Action()
{
Console.WriteLine("Receiver Action");
}
}
public class ConcreteCommand : ICommand
{
private 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 ExecuteCommand()
{
_command.Execute();
}
}
Interpreter design pattern
The Interpreter pattern defines a representation for a grammar and an interpreter to interpret sentences in the grammar.
public abstract class AbstractExpression
{
public abstract void Interpret(Context context);
}
public class TerminalExpression : AbstractExpression
{
public override void Interpret(Context context)
{
Console.WriteLine("TerminalExpression Interpret");
}
}
public class Context { }
Iterator design pattern
The Iterator pattern offers a means to sequentially access the elements of an aggregate object without revealing its internal structure.
public interface IIterator
{
bool HasNext();
object Next();
}
public interface IAggregate
{
IIterator CreateIterator();
}
public class ConcreteAggregate : IAggregate
{
private List<object> _items = new List<object>();
public IIterator CreateIterator()
{
return new ConcreteIterator(this);
}
public int Count => _items.Count;
public object this[int index] => _items[index];
public void Add(object item) => _items.Add(item);
}
public class ConcreteIterator : IIterator
{
private ConcreteAggregate _aggregate;
private int _current = 0;
public ConcreteIterator(ConcreteAggregate aggregate)
{
_aggregate = aggregate;
}
public bool HasNext() => _current < _aggregate.Count;
public object Next() => _aggregate[_current++];
}
Mediator design pattern
The Mediator pattern defines an object that encapsulates the interactions among a group of objects.
public abstract class Mediator
{
public abstract void Send(string message, Colleague colleague);
}
public abstract class Colleague
{
protected Mediator mediator;
public Colleague(Mediator mediator)
{
this.mediator = mediator;
}
}
public class ConcreteColleague1 : Colleague
{
public ConcreteColleague1(Mediator mediator) : base(mediator) { }
public void Send(string message)
{
mediator.Send(message, this);
}
public void Receive(string message)
{
Console.WriteLine("Colleague1 received message: " + message);
}
}
public class ConcreteMediator : Mediator
{
public ConcreteColleague1 Colleague1 { get; set; }
public ConcreteColleague2 Colleague2 { get; set; }
public override void Send(string message, Colleague colleague)
{
if (colleague == Colleague1)
{
Colleague2.Receive(message);
}
else
{
Colleague1.Receive(message);
}
}
}
Memento design pattern
The Memento pattern provides the ability to restore an object to its previous state.
public class Memento
{
public string State { get; private set; }
public Memento(string state)
{
State = state;
}
}
public class Originator
{
public string State { get; set; }
public Memento CreateMemento()
{
return new Memento(State);
}
public void SetMemento(Memento memento)
{
State = memento.State;
}
}
public class Caretaker
{
private Memento _memento;
public Memento Memento
{
get { return _memento; }
set { _memento = value; }
}
}
Observer design pattern
The Observer pattern establishes a one-to-many dependency between objects, ensuring that when one object changes state, all of its dependents are notified.
public abstract class Observer
{
public abstract void Update();
}
public class ConcreteObserver : Observer
{
private string _observerState;
private ConcreteSubject _subject;
public ConcreteObserver(ConcreteSubject subject)
{
_subject = subject;
}
public override void Update()
{
_observerState = _subject.SubjectState;
Console.WriteLine("Observer state updated to: " + _observerState);
}
}
public abstract class Subject
{
private List<Observer> _observers = new List<Observer>();
public void Attach(Observer observer)
{
_observers.Add(observer);
}
public void Detach(Observer observer)
{
_observers.Remove(observer);
}
public void Notify()
{
foreach (var observer in _observers)
{
observer.Update();
}
}
}
public class ConcreteSubject : Subject
{
private string _subjectState;
public string SubjectState
{
get { return _subjectState; }
set
{
_subjectState = value;
Notify();
}
}
}
State design pattern
The State pattern enables an object to modify its behavior when its internal state changes.
public abstract class State
{
public abstract void Handle(Context context);
}
public class ConcreteStateA : State
{
public override void Handle(Context context)
{
context.State = new ConcreteStateB();
}
}
public class ConcreteStateB : State
{
public override void Handle(Context context)
{
context.State = new ConcreteStateA();
}
}
public class Context
{
private State _state;
public Context(State state)
{
State = state;
}
public State State
{
get { return _state; }
set
{
_state = value;
Console.WriteLine("State: " + _state.GetType().Name);
}
}
public void Request()
{
_state.Handle(this);
}
}
Strategy design pattern
The Strategy pattern defines a family of algorithms, encapsulates each one, and enables them to be interchangeable.
public interface IStrategy
{
void AlgorithmInterface();
}
public class ConcreteStrategyA : IStrategy
{
public void AlgorithmInterface()
{
Console.WriteLine("Strategy A");
}
}
public class ConcreteStrategyB : IStrategy
{
public void AlgorithmInterface()
{
Console.WriteLine("Strategy B");
}
}
public class Context
{
private IStrategy _strategy;
public Context(IStrategy strategy)
{
_strategy = strategy;
}
public void ContextInterface()
{
_strategy.AlgorithmInterface();
}
}
Template Method design pattern
The Template Method pattern outlines the framework of an algorithm within an operation, delegating certain steps to subclasses.
public abstract class AbstractClass
{
public void TemplateMethod()
{
PrimitiveOperation1();
PrimitiveOperation2();
}
protected abstract void PrimitiveOperation1();
protected abstract void PrimitiveOperation2();
}
public class ConcreteClass : AbstractClass
{
protected override void PrimitiveOperation1()
{
Console.WriteLine("Primitive Operation 1");
}
protected override void PrimitiveOperation2()
{
Console.WriteLine("Primitive Operation 2");
}
}
Visitor design pattern
The Visitor pattern represents an operation to be executed on the elements of an object structure.
public interface IVisitor
{
void VisitConcreteElementA(ConcreteElementA element);
void VisitConcreteElementB(ConcreteElementB element);
}
public class ConcreteVisitor : IVisitor
{
public void VisitConcreteElementA(ConcreteElementA element)
{
Console.WriteLine("Visited ConcreteElementA");
}
public void VisitConcreteElementB(ConcreteElementB element)
{
Console.WriteLine("Visited ConcreteElementB");
}
}
public abstract class Element
{
public abstract void Accept(IVisitor visitor);
}
public class ConcreteElementA : Element
{
public override void Accept(IVisitor visitor)
{
visitor.VisitConcreteElementA(this);
}
}
public class ConcreteElementB : Element
{
public override void Accept(IVisitor visitor)
{
visitor.VisitConcreteElementB(this);
}
}
Conclusion
Design patterns are powerful tools for designing robust, reusable, and maintainable code. This post provided an overview of various design patterns in C# with examples. Each pattern addresses different problems and offers unique solutions, making them indispensable in a software engineer’s toolkit. For a deeper understanding, detailed posts for each pattern will follow, exploring their use cases and implementations more thoroughly. Stay tuned!
Service Tag: – Csharpmaster | Csharp Master | c# quiz | Fluent Validation in .NET Core | monolithic and microservices architecture | Global Exception Handler in .NET Core | HashMap in C# | Dictionary in C# | split string in c# |Open Closed Principle in C#| liskov substitution principle c# example | Difference Between Async and Await in C# | Difference Between String and StringBuilder in C# | What is Delegate in C# with Example | dependency injection in .NET Core | Lambda Expression in C# | Design Patterns in C# with Examples | Boxing and Unboxing in C# with Example | Collection in C# with Example | How to split string in ca comprehensive guide | Implement open closed principle in csharp example | Difference between async and await in c with-example | string and stringbuilder in csharp with example csharp master tutorial | lambda expression in c with example
- Enum Data Type in PostgreSQL with Practical Examples
- Understanding the Repository Design Pattern in C#
- What is DTO in C# with Example | Data Transfer Object Design Pattern | C# Master
- Understanding POCO Class in C#
- CSharpMaster’s C# Quiz for Beginner
- Using Fluent Validation in .NET Core 8 with Example
- CsharpMaster – Difference Between Monolithic and Microservices Architecture with Example
- Csharp Master – Global Exception Handler in .NET Core 7 & .Net Core 8 Web API (2024)
- Understanding What is HashMap in C# with Example (2024)
- CSharp Master’s Guide to Using Dictionary in C# (Key-value pair) – 2024
- CSharpMaster – How to Split String in C# (String.Split() with string delimiter)
- CSharpMaster – How to Implement Open Closed Principle in C# Example (SOLID)
- Understanding liskov principle c# | liskov substitution principle c# example
- Difference Between Async and Await in C# with Example – C# asynchronous programming 2024
- Difference Between String and StringBuilder in C# with example – Csharp Master Tutorial