Design patterns are a set of solutions to common programming problems that have been tested and proven over time. In C#, there are several design patterns that you can use to improve your code and make it more maintainable, readable, and scalable. In this article, we will discuss some of the most popular design patterns in C# and how you can use them in your code.
Read Also- SOLID Principles in C#
- Singleton Pattern
The Singleton pattern is used when you want to ensure that only one instance of a class is created and that it is accessible from anywhere in your code. To implement the Singleton pattern in C#, you can use the following code:
public class Singleton
{
private static Singleton instance;
private Singleton() { }
public static Singleton Instance
{
get
{
if (instance == null)
{
instance = new Singleton();
}
return instance;
}
}
}
In this example, we have a private constructor that ensures that no other instances of the Singleton class can be created. We also have a static variable called instance that holds the only instance of the Singleton class. Finally, we have a public static property called Instance that returns the only instance of the Singleton class.
- Factory Pattern
The Factory pattern is used when you want to create objects without exposing the instantiation logic to the client. To implement the Factory pattern in C#, you can use the following code:
public interface IProduct
{
void Operation();
}
public class ConcreteProductA : IProduct
{
public void Operation()
{
Console.WriteLine("ConcreteProductA.Operation()");
}
}
public class ConcreteProductB : IProduct
{
public void Operation()
{
Console.WriteLine("ConcreteProductB.Operation()");
}
}
public class Factory
{
public static IProduct CreateProduct(string type)
{
switch (type)
{
case "A":
return new ConcreteProductA();
case "B":
return new ConcreteProductB();
default:
throw new ArgumentException("Invalid type.");
}
}
}
In this example, we have an interface called IProduct that defines the operation that our products will perform. We also have two classes called ConcreteProductA and ConcreteProductB that implement the IProduct interface. Finally, we have a Factory class that creates instances of ConcreteProductA and ConcreteProductB based on the type parameter passed to the CreateProduct method.
- Observer Pattern
The Observer pattern is used when you want to notify a set of objects when a change occurs in another object. To implement the Observer pattern in C#, you can use the following code:
public interface IObserver
{
void Update();
}
public interface IObservable
{
void Attach(IObserver observer);
void Detach(IObserver observer);
void Notify();
}
public class ConcreteObservable : IObservable
{
private 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()
{
foreach (IObserver observer in observers)
{
observer.Update();
}
}
}
public class ConcreteObserver : IObserver
{
public void Update()
{
Console.WriteLine("ConcreteObserver.Update()");
}
}
In this example, we have an interface called IObserver that defines the Update method that our observers will implement. We also have an interface called IObservable that defines the Attach, Detach, and Notify methods that our observables will implement. Finally, we have a ConcreteObservable class that maintains a list of observers and notifies them when a change occurs
- Decorator Pattern
The Decorator pattern is used when you want to add functionality to an object dynamically, without affecting other objects of the same class. To implement the Decorator pattern in C#, you can use the following code:
public interface IComponent
{
void Operation();
}
public class ConcreteComponent : IComponent
{
public void Operation()
{
Console.WriteLine("ConcreteComponent.Operation()");
}
}
public abstract class Decorator : IComponent
{
private IComponent component;
public Decorator(IComponent component)
{
this.component = component;
}
public void Operation()
{
component.Operation();
}
}
public class ConcreteDecoratorA : Decorator
{
public ConcreteDecoratorA(IComponent component) : base(component) { }
public void AddedBehavior()
{
Console.WriteLine("ConcreteDecoratorA.AddedBehavior()");
}
public override void Operation()
{
base.Operation();
AddedBehavior();
}
}
public class ConcreteDecoratorB : Decorator
{
public ConcreteDecoratorB(IComponent component) : base(component) { }
public void AddedBehavior()
{
Console.WriteLine("ConcreteDecoratorB.AddedBehavior()");
}
public override void Operation()
{
base.Operation();
AddedBehavior();
}
}
In this example, we have an interface called IComponent that defines the Operation method that our components will implement. We also have a ConcreteComponent class that implements the IComponent interface. Finally, we have two decorator classes called ConcreteDecoratorA and ConcreteDecoratorB that add functionality to our ConcreteComponent class.
- Adapter Pattern
The Adapter pattern is used when you want to convert the interface of one class into another interface that clients expect. To implement the Adapter pattern in C#, you can use the following code:
public interface ITarget
{
void Request();
}
public class Adaptee
{
public void SpecificRequest()
{
Console.WriteLine("Adaptee.SpecificRequest()");
}
}
public class Adapter : ITarget
{
private Adaptee adaptee = new Adaptee();
public void Request()
{
adaptee.SpecificRequest();
}
}
In this example, we have an interface called ITarget that defines the Request method that our clients will use. We also have a class called Adaptee that has a method called SpecificRequest that we want to adapt to the ITarget interface. Finally, we have a class called Adapter that implements the ITarget interface and adapts the SpecificRequest method of the Adaptee class.
- Facade Pattern
The Facade pattern is used when you want to provide a simplified interface to a complex system. To implement the Facade pattern in C#, you can use the following code:
public class SubsystemA
{
public void OperationA()
{
Console.WriteLine("SubsystemA.OperationA()");
}
}
public class SubsystemB
{
public void OperationB()
{
Console.WriteLine("SubsystemB.OperationB()");
}
}
public class Facade
{
private SubsystemA subsystemA = new SubsystemA();
private SubsystemB subsystemB = new SubsystemB();
public void Operation()
{
subsystemA.OperationA();
subsystemB.OperationB();
}
}
In this example, we have two subsystems called SubsystemA and SubsystemB that have their own set of operations. We also have a Facade class that simplifies the interface to these subsystems by providing a single Operation method that calls the relevant operations of the subsystems
- Proxy Pattern
The Proxy pattern is used when you want to control access to an object. It provides a surrogate or placeholder for another object to control access to it. To implement the Proxy pattern in C#, you can use the following code:
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();
}
}
In this example, we have an interface called ISubject that defines the Request method that our clients will use. We also have a RealSubject class that implements the ISubject interface and the actual implementation of the Request method. Finally, we have a Proxy class that also implements the ISubject interface and controls access to the RealSubject object by creating it only when necessary.
- Template Method Pattern
The Template Method pattern is used when you want to define the skeleton of an algorithm in a base class and let subclasses override specific steps of the algorithm without changing its structure. To implement the Template Method pattern in C#, you can use the following code:
public abstract class AbstractClass
{
public void TemplateMethod()
{
Operation1();
Operation2();
}
protected abstract void Operation1();
protected abstract void Operation2();
}
public class ConcreteClassA : AbstractClass
{
protected override void Operation1()
{
Console.WriteLine("ConcreteClassA.Operation1()");
}
protected override void Operation2()
{
Console.WriteLine("ConcreteClassA.Operation2()");
}
}
public class ConcreteClassB : AbstractClass
{
protected override void Operation1()
{
Console.WriteLine("ConcreteClassB.Operation1()");
}
protected override void Operation2()
{
Console.WriteLine("ConcreteClassB.Operation2()");
}
}
In this example, we have an abstract class called AbstractClass that defines the TemplateMethod that contains the algorithm’s skeleton. We also have two concrete classes called ConcreteClassA and ConcreteClassB that inherit from the AbstractClass and implement their own versions of the Operation1 and Operation2 methods.
- Observer Pattern
The Observer pattern is used when you want to establish a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically. To implement the Observer pattern in C#, you can use the following code:
public interface IObserver
{
void Update();
}
public interface ISubject
{
void Attach(IObserver observer);
void Detach(IObserver observer);
void Notify();
}
public class ConcreteSubject : ISubject
{
private 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()
{
foreach (IObserver observer in observers)
{
observer.Update();
}
}
}
public class ConcreteObserver : IObserver
{
public void Update()
{
Console.WriteLine("ConcreteObserver.Update()");
}
}
In this example, we have an interface called IObserver that defines the Update method that our observers will implement. We also have an interface called ISubject that defines the Attach, Detach, and Notify methods that our subject will implement. Finally, we have two concrete classes called ConcreteSubject and Concrete
- State Pattern
The State pattern is used when you want to change the behavior of an object based on its internal state. It encapsulates the state in a separate class and delegates the behavior to it. To implement the State pattern in C#, you can use the following code:
public interface IState
{
void Handle();
}
public class ConcreteStateA : IState
{
public void Handle()
{
Console.WriteLine("ConcreteStateA.Handle()");
}
}
public class ConcreteStateB : IState
{
public void Handle()
{
Console.WriteLine("ConcreteStateB.Handle()");
}
}
public class Context
{
private IState state;
public void SetState(IState state)
{
this.state = state;
}
public void Request()
{
state.Handle();
}
}
In this example, we have an interface called IState that defines the Handle method that our state classes will implement. We also have two concrete state classes called ConcreteStateA and ConcreteStateB that implement their own versions of the Handle method. Finally, we have a Context class that holds a reference to the current state object and delegates the behavior to it.
- Visitor Pattern
The Visitor pattern is used when you want to separate the algorithm from the object structure it operates on. It encapsulates the algorithm in a separate class and passes it to the objects to operate on. To implement the Visitor pattern in C#, you can use the following code:
public interface IVisitor
{
void Visit(ConcreteElementA element);
void Visit(ConcreteElementB element);
}
public interface IElement
{
void Accept(IVisitor visitor);
}
public class ConcreteElementA : IElement
{
public void Accept(IVisitor visitor)
{
visitor.Visit(this);
}
public void OperationA()
{
Console.WriteLine("ConcreteElementA.OperationA()");
}
}
public class ConcreteElementB : IElement
{
public void Accept(IVisitor visitor)
{
visitor.Visit(this);
}
public void OperationB()
{
Console.WriteLine("ConcreteElementB.OperationB()");
}
}
public class ConcreteVisitor : IVisitor
{
public void Visit(ConcreteElementA element)
{
element.OperationA();
}
public void Visit(ConcreteElementB element)
{
element.OperationB();
}
}
In this example, we have an interface called IVisitor that defines the Visit method that our visitor class will implement. We also have an interface called IElement that defines the Accept method that our elements will implement. Finally, we have two concrete element classes called ConcreteElementA and ConcreteElementB that implement their own versions of the Accept method, and a concrete visitor class called ConcreteVisitor that implements the Visit method to operate on the elements
- Iterator Pattern
The Iterator pattern is used when you want to traverse a collection of objects without exposing its underlying implementation. It provides a way to access the elements of an aggregate object sequentially without exposing its internal representation. To implement the Iterator pattern in C#, you can use the following code:
public interface IIterator
{
bool HasNext();
object Next();
}
public interface IAggregate
{
IIterator CreateIterator();
}
public class ConcreteIterator : IIterator
{
private ConcreteAggregate aggregate;
private int index = 0;
public ConcreteIterator(ConcreteAggregate aggregate)
{
this.aggregate = aggregate;
}
public bool HasNext()
{
return index < aggregate.Count;
}
public object Next()
{
object result = aggregate[index];
index++;
return result;
}
}
public class ConcreteAggregate : IAggregate
{
private List<object> items = new List<object>();
public IIterator CreateIterator()
{
return new ConcreteIterator(this);
}
public int Count
{
get { return items.Count; }
}
public object this[int index]
{
get { return items[index]; }
set { items.Insert(index, value); }
}
}
In this example, we have an interface called IIterator that defines the HasNext and Next methods that our iterator class will implement. We also have an interface called IAggregate that defines the CreateIterator method that our aggregate class will implement. Finally, we have a concrete iterator class called ConcreteIterator that implements the IIterator interface and a concrete aggregate class called ConcreteAggregate that implements the IAggregate interface.
- Facade Pattern
The Facade pattern is used when you want to provide a simplified interface to a complex system. It provides a unified interface to a set of interfaces in a subsystem. To implement the Facade pattern in C#, you can use the following code:
public class SubsystemA
{
public void OperationA()
{
Console.WriteLine("SubsystemA.OperationA()");
}
}
public class SubsystemB
{
public void OperationB()
{
Console.WriteLine("SubsystemB.OperationB()");
}
}
public class Facade
{
private SubsystemA subsystemA;
private SubsystemB subsystemB;
public Facade()
{
subsystemA = new SubsystemA();
subsystemB = new SubsystemB();
}
public void OperationAB()
{
subsystemA.OperationA();
subsystemB.OperationB();
}
}
In this example, we have two subsystems called SubsystemA and SubsystemB, each with their own set of operations. We also have a Facade class that provides a simplified interface to both subsystems. The Facade class holds references to both subsystems and delegates the calls to their respective methods.
2 thoughts on “Design Patterns in C#”