Open In App

Design Patterns in C sharp (C#)

Last Updated : 18 Jan, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

Design patterns in C Sharp (C#) help developers create maintainable, flexible, and understandable code. They encapsulate the expertise and experience of seasoned software architects and developers, making it easier for newer programmers to follow established best practices.

1. What are Design Patterns in C sharp (C#)?

Design patterns are defined as reusable solutions to the common problems that arise during software design and development. They are general templates or best practices that guide developers in creating well-structured, maintainable, and efficient code.

2. Types of Design Patterns in C sharp (C#)

Several types of design patterns are commonly used in software development. These patterns can be categorized into three main groups:

  • Creational Design Patterns
  • Structural Design Patterns
  • Behavioral Design Patterns

Types-of-Design-Patterns-in-C

3.Creational Design Patterns in C sharp (C#)

Creational design patterns abstract the instantiation process. They help make a system independent of how its objects are created, composed, and represented. A class creational pattern uses inheritance to vary the class that’s instantiated, whereas an object creational pattern will delegate instantiation to another object. Creational patterns give a lot of flexibility in what gets created, who creates it, how it gets created, and, when

There are two recurring themes in these patterns: 

  • They all encapsulate knowledge about which concrete class the system uses. 
  • They hide how instances of these classes are created and put together.

3.1 Factory Method Design Pattern in C sharp (C#)

Factory Method or Factory Design Patterns also known as virtual constructor, it define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses. 

When to use Factory Method

  • A class can’t anticipate the class of objects it must create.
  • A class wants its subclass to specify the objects it creates. 
  • Classes delegate responsibility to one of several helper subclasses, and you want to localize the knowledge of which helper subclass is the delegate.

Let’s understand Factory Method by using an example:

When making a system where clients can request different types of vehicles (TwoWheeler, ThreeWheeler, or FourWheeler) , this might involves creating instances of concrete classes directly in the client code.

  • If multiple clients need to create instances of vehicles, the code for creating those instances might be duplicated across different parts of the application.
  • Creating instances of concrete classes directly ties the client code to those specific classes.
  • The Client code could become cluttered with the details of object creation, making it harder to read and understand.

Then how Factory Pattern provides a centralized and abstract way to create objects for the above problem?

In the below code the VehicleFactory class is responsible for creating instances of the requested vehicles based on the provided VehicleType. The client class (Client) uses the factory to build vehicles and then interacts with the created vehicles without knowing their specific implementations.

Below is the Code for the above problem statement:

C#




using System;
 
// Enum defining vehicle types
public enum VehicleType
{
    TwoWheeler,
    ThreeWheeler,
    FourWheeler
}
 
// Abstract class for Vehicle
public abstract class Vehicle
{
    public abstract void PrintVehicleInfo();
}
 
// Concrete classes for each type of vehicle
public class TwoWheeler : Vehicle
{
    public override void PrintVehicleInfo()
    {
        Console.WriteLine("I am two wheeler");
    }
}
 
public class ThreeWheeler : Vehicle
{
    public override void PrintVehicleInfo()
    {
        Console.WriteLine("I am three wheeler");
    }
}
 
public class FourWheeler : Vehicle
{
    public override void PrintVehicleInfo()
    {
        Console.WriteLine("I am four wheeler");
    }
}
 
// Interface for the VehicleFactory
public interface IVehicleFactory
{
    Vehicle Build(VehicleType vehicleType);
}
 
// Concrete class implementing the VehicleFactory
public class VehicleFactory : IVehicleFactory
{
    public Vehicle Build(VehicleType vehicleType)
    {
        switch (vehicleType)
        {
            case VehicleType.TwoWheeler:
                return new TwoWheeler();
            case VehicleType.ThreeWheeler:
                return new ThreeWheeler();
            case VehicleType.FourWheeler:
                return new FourWheeler();
            default:
                return null;
        }
    }
}
 
// Client class
public class Client
{
    private Vehicle _pVehicle;
 
    public Client()
    {
        _pVehicle = null;
    }
 
    public void BuildVehicle(VehicleType vehicleType)
    {
        IVehicleFactory vf = new VehicleFactory();
        _pVehicle = vf.Build(vehicleType);
    }
 
    public Vehicle GetVehicle()
    {
        return _pVehicle;
    }
}
 
// Driver Program
class Program
{
    static void Main(string[] args)
    {
        Client client = new Client();
 
        client.BuildVehicle(VehicleType.TwoWheeler);
        client.GetVehicle().PrintVehicleInfo();
 
        client.BuildVehicle(VehicleType.ThreeWheeler);
        client.GetVehicle().PrintVehicleInfo();
 
        client.BuildVehicle(VehicleType.FourWheeler);
        client.GetVehicle().PrintVehicleInfo();
    }
}


Output

I am two wheeler
I am three wheeler
I am four wheeler






3.2 Abstract Factory Method Design Pattern in C sharp (C#) 

Abstract Factory design pattern is one of the Creational pattern. Abstract Factory pattern is almost similar to Factory Pattern and is considered as another layer of abstraction over factory pattern. Abstract Factory patterns work around a super-factory which creates other factories.

Let’s understand Abstract Factory Method by using an example:

When designing a system that allows the creation of different types of cars (Micro, Mini, Luxury) in various locations (USA, India, Default). Each type of car and location combination has its own factory.

  • Direct instantiation of car objects without an abstract factory would tightly couple the client code to concrete car classes and their instantiation logic.
  • Adding a new type of car or a new location would require modifying the existing client code, violating the Open/Closed Principle of software design.
  • The lack of a centralized and abstract way to create objects can reduce code readability and maintainability.

How we Abstract Factory Pattern gonna solve the above problem?

The Below code provides an abstract base class Car and concrete implementations for each type of car. There are also separate factory classes for different locations (INDIACarFactory, USACarFactory, and DefaultCarFactory). The CarFactory class acts as an abstract factory that decides which concrete factory to use based on the specified location.

Below is the Code for the above problem statement:

C#




using System;
 
public enum CarType
{
    MICRO, MINI, LUXURY
}
 
public enum Location
{
    DEFAULT, USA, INDIA
}
 
public abstract class Car
{
    protected Car(CarType model, Location location)
    {
        Model = model;
        Location = location;
    }
 
    public CarType Model { get; }
    public Location Location { get; }
 
    public abstract void Construct();
 
    public override string ToString()
    {
        return $"CarModel - {Model} located in {Location}";
    }
}
 
public class LuxuryCar : Car
{
    public LuxuryCar(Location location) : base(CarType.LUXURY, location)
    {
        Construct();
    }
 
    public override void Construct()
    {
        Console.WriteLine("Connecting to luxury car");
    }
}
 
public class MicroCar : Car
{
    public MicroCar(Location location) : base(CarType.MICRO, location)
    {
        Construct();
    }
 
    public override void Construct()
    {
        Console.WriteLine("Connecting to Micro Car ");
    }
}
 
public class MiniCar : Car
{
    public MiniCar(Location location) : base(CarType.MINI, location)
    {
        Construct();
    }
 
    public override void Construct()
    {
        Console.WriteLine("Connecting to Mini car");
    }
}
 
public class INDIACarFactory
{
    public static Car BuildCar(CarType model)
    {
        switch (model)
        {
            case CarType.MICRO:
                return new MicroCar(Location.INDIA);
 
            case CarType.MINI:
                return new MiniCar(Location.INDIA);
 
            case CarType.LUXURY:
                return new LuxuryCar(Location.INDIA);
 
            default:
                return null;
        }
    }
}
 
public class DefaultCarFactory
{
    public static Car BuildCar(CarType model)
    {
        switch (model)
        {
            case CarType.MICRO:
                return new MicroCar(Location.DEFAULT);
 
            case CarType.MINI:
                return new MiniCar(Location.DEFAULT);
 
            case CarType.LUXURY:
                return new LuxuryCar(Location.DEFAULT);
 
            default:
                return null;
        }
    }
}
 
public class USACarFactory
{
    public static Car BuildCar(CarType model)
    {
        switch (model)
        {
            case CarType.MICRO:
                return new MicroCar(Location.USA);
 
            case CarType.MINI:
                return new MiniCar(Location.USA);
 
            case CarType.LUXURY:
                return new LuxuryCar(Location.USA);
 
            default:
                return null;
        }
    }
}
 
public class CarFactory
{
    private CarFactory()
    {
    }
 
    public static Car BuildCar(CarType type)
    {
        Location location = Location.INDIA;
 
        switch (location)
        {
            case Location.USA:
                return USACarFactory.BuildCar(type);
 
            case Location.INDIA:
                return INDIACarFactory.BuildCar(type);
 
            default:
                return DefaultCarFactory.BuildCar(type);
        }
    }
}
 
public class AbstractDesign
{
    public static void Main(string[] args)
    {
        Console.WriteLine(CarFactory.BuildCar(CarType.MICRO));
        Console.WriteLine(CarFactory.BuildCar(CarType.MINI));
        Console.WriteLine(CarFactory.BuildCar(CarType.LUXURY));
    }
}


Output

Connecting to Micro Car 
CarModel - MICRO located in INDIA
Connecting to Mini car
CarModel - MINI located in INDIA
Connecting to luxury car
CarModel - LUXURY located in INDIA






3.3 Singleton Method Design Pattern in C sharp (C#)

Singleton pattern is a design pattern which restricts a class to instantiate its multiple objects. It is nothing but a way of defining a class.

  • Class is defined in such a way that only one instance of the class is created in the complete execution of a program or project.
  • When the sole instance should be extensible by subclassing and clients should be able to use an extended instance without modifying 
  • Singleton classes are used for logging, driver objects, caching, and thread pool, database connections.

Let’s understand Singleton Method by using an example:

  • In some scenarios where we need a single point of control for a global state or shared resource, where we have to manage and coordinate changes across multiple instances.
  • In some scenarios where we unintentionally create multiple instances of a class, leading to redundant memory usage and potential inconsistencies in data or behavior. Lack of a global access point can make it difficult for different parts of the application to interact with a common instance, leading to redundancy and a lack of centralized control.

How we are gonna solve this problem using Singleton Pattern?

The Singleton class has a private constructor, a private static instance, and a public static method GetInstance() that returns the instance. The method demonstrates the usage of the Singleton pattern by calling the GetInstance() method and then invoking the DoSomething() method on the obtained instance.

Below is the Code for the above problem statement:

C#




using System;
 
public class Singleton
{
    // static class
    private static Singleton instance;
 
    private Singleton()
    {
        Console.WriteLine("Singleton is Instantiated.");
    }
 
    public static Singleton GetInstance()
    {
        if (instance == null)
            instance = new Singleton();
        return instance;
    }
 
    public void DoSomething()
    {
        Console.WriteLine("Something is Done.");
    }
}
 
class Program
{
    static void Main(string[] args)
    {
        Singleton.GetInstance().DoSomething();
    }
}


Output

Singleton is Instantiated.
Something is Done.






3.4 Prototype Method Design Pattern in C sharp (C#)

Prototype allows us to hide the complexity of making new instances from the client. The concept is to copy an existing object rather than creating a new instance from scratch, something that may include costly operations. The existing object acts as a prototype and contains the state of the object. The newly copied object may change same properties only if required. This approach saves costly resources and time, especially when object creation is a heavy process.

Let’s understand Prototype Method by using an example:

Lets consider a scenario where you are developing a system for an online graphic design application. Users can create various graphic elements, such as logos, icons, or templates. Each graphic element can be customized in terms of colors, sizes, and styles. The challenge is to efficiently create new graphic elements while allowing users to customize and iterate upon existing designs.

How Prototype design pattern help to solve this problem?

The Prototype design pattern allows us to create new objects by copying an existing object, known as the prototype. In this scenario, we can define a prototype interface, say GraphicElement, which declares a method to clone the object. Concrete classes like Logo, Icon, and Template implement this interface.

Below is the Code for the above problem statement:

C#




using System;
 
// Interface for the prototype
public interface IGraphicElement
{
    IGraphicElement Clone();
    void Display();
    // Other methods for customization can be added here
}
 
// Concrete prototype representing a graphic element
public class Logo : IGraphicElement
{
    private string name;
    private string color;
 
    public Logo(string name, string color)
    {
        this.name = name;
        this.color = color;
    }
 
    public IGraphicElement Clone()
    {
        // Create a shallow copy of the object
        return (IGraphicElement)MemberwiseClone();
    }
 
    public void Display()
    {
        Console.WriteLine($"Logo: {name}, Color: {color}");
    }
 
    // Additional methods for customization can be added here
}
 
// Client code demonstrating the use of the Prototype pattern
class Program
{
    static void Main()
    {
        // Create an initial logo prototype
        IGraphicElement originalLogo = new Logo("OriginalLogo", "Blue");
         
        // Clone the original logo to create a new customized logo
        IGraphicElement customizedLogo = originalLogo.Clone();
         
        // Display the original and customized logos
        Console.WriteLine("Original Logo:");
        originalLogo.Display();
 
        Console.WriteLine("\nCustomized Logo:");
        customizedLogo.Display();
    }
}


Output

Original Logo:
Logo: OriginalLogo, Color: Blue

Customized Logo:
Logo: OriginalLogo, Color: Blue






3.5 Builder Method Design Pattern in C sharp (C#)

Builder pattern aims to “Separate the construction of a complex object from its representation so that the same construction process can create different representations.” It is used to construct a complex object step by step and the final step will return the object. The process of constructing an object should be generic so that it can be used to create different representations of the same object.

Let’s understand Builder Method by using an example:

Lets consider a system for constructing houses with different features, The house class represents a house with various components like basement, structure, roof, and interior , The House class might have a complex constructor with numerous parameters representing different components and their configurations. This can lead to confusion and errors, especially when there are many optional parameters.

How Builder Pattern gonna help to solve this problem:

The builder design pattern, we can address these problems, providing a clear and concise way to construct objects with many optional components while improving code readability, maintainability, and flexibility.

In the below code:

  • There are two types of builders: IglooHouseBuilder and TipiHouseBuilder. Each builder knows how to build a specific type of house with its unique features.
  • The CivilEngineer class is responsible for orchestrating the construction process, using a specific builder to build a house with the desired components.
  • The main program (Builder) showcases the construction of an igloo house by creating an instance of IglooHouseBuilder, passing it to a CivilEngineer, and then instructing the engineer to construct the house.
  • The resulting house is then printed out.

Below is the Code for the above problem statement:

C#




using System;
 
// HousePlan interface
public interface IHousePlan
{
    void SetBasement(string basement);
    void SetStructure(string structure);
    void SetRoof(string roof);
    void SetInterior(string interior);
}
 
// House class implementing HousePlan interface
public class House : IHousePlan
{
    private string basement;
    private string structure;
    private string roof;
    private string interior;
 
    public void SetBasement(string basement)
    {
        this.basement = basement;
    }
 
    public void SetStructure(string structure)
    {
        this.structure = structure;
    }
 
    public void SetRoof(string roof)
    {
        this.roof = roof;
    }
 
    public void SetInterior(string interior)
    {
        this.interior = interior;
    }
 
    public override string ToString()
    {
        return $"Basement: {basement}, Structure: {structure}, Roof: {roof}, Interior: {interior}";
    }
}
 
// HouseBuilder interface
public interface IHouseBuilder
{
    void BuildBasement();
    void BuildStructure();
    void BuildRoof();
    void BuildInterior();
    House GetHouse();
}
 
// IglooHouseBuilder implementing HouseBuilder interface
public class IglooHouseBuilder : IHouseBuilder
{
    private House house;
 
    public IglooHouseBuilder()
    {
        this.house = new House();
    }
 
    public void BuildBasement()
    {
        house.SetBasement("Ice Bars");
    }
 
    public void BuildStructure()
    {
        house.SetStructure("Ice Blocks");
    }
 
    public void BuildRoof()
    {
        house.SetRoof("Ice Dome");
    }
 
    public void BuildInterior()
    {
        house.SetInterior("Ice Carvings");
    }
 
    public House GetHouse()
    {
        return house;
    }
}
 
// TipiHouseBuilder implementing HouseBuilder interface
public class TipiHouseBuilder : IHouseBuilder
{
    private House house;
 
    public TipiHouseBuilder()
    {
        this.house = new House();
    }
 
    public void BuildBasement()
    {
        house.SetBasement("Wooden Poles");
    }
 
    public void BuildStructure()
    {
        house.SetStructure("Wood and Ice");
    }
 
    public void BuildRoof()
    {
        house.SetRoof("Wood, caribou and seal skins");
    }
 
    public void BuildInterior()
    {
        house.SetInterior("Fire Wood");
    }
 
    public House GetHouse()
    {
        return house;
    }
}
 
// CivilEngineer class
public class CivilEngineer
{
    private IHouseBuilder houseBuilder;
 
    public CivilEngineer(IHouseBuilder houseBuilder)
    {
        this.houseBuilder = houseBuilder;
    }
 
    public House GetHouse()
    {
        return houseBuilder.GetHouse();
    }
 
    public void ConstructHouse()
    {
        houseBuilder.BuildBasement();
        houseBuilder.BuildStructure();
        houseBuilder.BuildRoof();
        houseBuilder.BuildInterior();
    }
}
 
// Main class
class Builder
{
    public static void Main(string[] args)
    {
        IHouseBuilder iglooBuilder = new IglooHouseBuilder();
        CivilEngineer engineer = new CivilEngineer(iglooBuilder);
 
        engineer.ConstructHouse();
 
        House house = engineer.GetHouse();
 
        Console.WriteLine("Builder constructed: " + house);
    }
}


Output

Builder constructed: Basement: Ice Bars, Structure: Ice Blocks, Roof: Ice Dome, Interior: Ice Carvings






4. Structural Design Patterns in C sharp (C#)

Structural Design Patterns are concerned with how classes and objects are composed to form larger structures. Structural class patterns use inheritance to compose interfaces or implementations. Consider how multiple inheritances mix two or more classes into one. The result is a class that combines the properties of its parent classes.

There are two recurring themes in these patterns: 

  • This pattern is particularly useful for making independently developed class libraries work together. 
  • Structural Design Patterns describe ways to compose objects to realize new functionality.
  • The added flexibility of object composition comes from the ability to change the composition at run-time, which is impossible with static class composition. 

4.1 Adapter Method Design Patterns in C sharp (C#) 

This pattern is easy to understand as the real world is full of adapters. 

For example:

consider a USB to Ethernet adapter. We need this when we have an Ethernet interface on one end and USB on the other. Since they are incompatible with each other. we use an adapter that converts one to other. This example is pretty analogous to Object Oriented Adapters.

In design, adapters are used when we have a class (Client) expecting some type of object and we have an object (Adaptee) offering the same features but exposing a different interface.

Let’s understand Adapter Method by using an example:

Lets consider you have an existing system that works with a service called LegacyService, which has a method LegacyRequest(). However, you need to integrate a new service called NewService, which has a method NewRequest().

  • The method names and signatures in LegacyService and NewService do not match, causing a direct incompatibility.
  • Modifying the existing system to work with NewService directly would require changes to its code, leading to disruptions and potentially breaking the existing functionality.

How Adapter Pattern gonna help to solve this problem:

The Adapter Pattern makes the NewService compatible with the ILegacyService interface expected by the existing system.

In the below code:

  • Target Interface: Define an interface called ILegacyService that represents the operations expected by the existing system.
  • Adaptee: Create a class called NewService with the method NewRequest().
  • Adapter Class:Create an adapter class Adapter that implements ILegacyService and wraps an instance of NewService. The Adapter class delegates calls to LegacyRequest() to the corresponding method in NewService.

Below is the Code for the above problem statement:

C#




using System;
 
// Target Interface
public interface ILegacyService
{
    void LegacyRequest();
}
 
// Adaptee
public class NewService
{
    public void NewRequest()
    {
        Console.WriteLine("Executing NewService.NewRequest()");
    }
}
 
// Adapter
public class Adapter : ILegacyService
{
    private readonly NewService newService;
 
    public Adapter(NewService newService)
    {
        this.newService = newService;
    }
 
    public void LegacyRequest()
    {
        newService.NewRequest();
    }
}
 
// Client Code
class Program
{
    static void Main()
    {
        // Using Adapter
        NewService newService = new NewService();
        ILegacyService legacyServiceAdapter = new Adapter(newService);
 
        // Client invokes LegacyRequest without knowing about NewService
        legacyServiceAdapter.LegacyRequest();
    }
}


Output

Executing NewService.NewRequest()






4.2 Bridge Method Design Patterns in C sharp (C#)

The bridge pattern allows the Abstraction and the Implementation to be developed independently and the client code can access only the Abstraction part without being concerned about the Implementation part.

Let’s understand Bridge Method by using an example:

Lets consider a system where different types of vehicles (cars and bikes) can be manufactured with different workshops (e.g., produce and assemble) but adding new types of vehicles or workshops becomes challenging, It might need to modify existing code, and introducing new variations could lead to code duplication and increased complexity.

It might have a high degree of coupling between the abstraction (e.g., vehicles) and its implementation (e.g., workshops). Any changes to one part of the system could affect the other, making it less flexible.

How Bridge Pattern gonna help to solve this problem:

By using the Bridge design pattern, you can address these challenges by separating abstraction from implementation. This allows for greater flexibility, extensibility, and maintainability in your system, making it easier to add new types of vehicles or workshops independently.

In the below code:

  • The Bridge pattern allows the abstraction (vehicle) and its implementation (workshop) to vary independently
  • There is a common abstraction, represented by the Vehicle class, which has references to two workshops.
  • Two abstractions, Car and Bike, extend the Vehicle class and implement the Manufacture method, combining the workshops to showcase the manufacturing process.
  • The IWorkshop interface defines the methods that workshops must implement, and two concrete implementations (Produce and Assemble) represent different steps in the manufacturing process.
  • The BridgePattern class demonstrates the usage of the Bridge pattern by creating instances of cars and bikes, associating them with specific workshops, and then manufacturing each vehicle, showing the steps of production.

Below is the Code for the above problem statement:

C#




using System;
 
// Abstraction in bridge pattern
abstract class Vehicle
{
    protected IWorkshop workShop1;
    protected IWorkshop workShop2;
 
    protected Vehicle(IWorkshop workShop1, IWorkshop workShop2)
    {
        this.workShop1 = workShop1;
        this.workShop2 = workShop2;
    }
 
    public abstract void Manufacture();
}
 
// Refine abstraction 1 in bridge pattern
class Car : Vehicle
{
    public Car(IWorkshop workShop1, IWorkshop workShop2) : base(workShop1, workShop2)
    {
    }
 
    public override void Manufacture()
    {
        Console.Write("Car ");
        workShop1.Work();
        workShop2.Work();
    }
}
 
// Refine abstraction 2 in bridge pattern
class Bike : Vehicle
{
    public Bike(IWorkshop workShop1, IWorkshop workShop2) : base(workShop1, workShop2)
    {
    }
 
    public override void Manufacture()
    {
        Console.Write("Bike ");
        workShop1.Work();
        workShop2.Work();
    }
}
 
// Implementer for bridge pattern
interface IWorkshop
{
    void Work();
}
 
// Concrete implementation 1 for bridge pattern
class Produce : IWorkshop
{
    public void Work()
    {
        Console.Write("Produced");
    }
}
 
// Concrete implementation 2 for bridge pattern
class Assemble : IWorkshop
{
    public void Work()
    {
        Console.Write(" And");
        Console.WriteLine(" Assembled.");
    }
}
 
// Demonstration of bridge design pattern
class BridgePattern
{
    static void Main(string[] args)
    {
        Vehicle vehicle1 = new Car(new Produce(), new Assemble());
        vehicle1.Manufacture();
 
        Vehicle vehicle2 = new Bike(new Produce(), new Assemble());
        vehicle2.Manufacture();
    }
}


Output

Car Produced And Assembled.
Bike Produced And Assembled.






4.3 Composite Method Design Patterns in C sharp (C#)

Composite pattern is a partitioning design pattern and describes a group of objects that is treated the same way as a single instance of the same type of object. The intent of a composite is to “compose” objects into tree structures to represent part-whole hierarchies. It allows you to have a tree structure and ask each node in the tree structure to perform a task.

Let’s understand Composite Method by using an example:

Lets consider a system where different types of employees (Developers and Managers) can be organized in a hierarchical structure but managing hierarchical structures becomes more complex, as you have to handle the distinctions between employees and groups separately. Operations that should be uniform for both individuals and groups may need to be duplicated with slight modifications, leading to maintenance challenges.

How Composite Pattern gonna help to solve this problem:

By applying the Composite design pattern, you can address these challenges, allowing you to treat both individual employees and groups uniformly, simplifying your code, and providing a more consistent and scalable solution.

In the below code:

  • A common interface, IEmployee, which all employee classes implement. This interface includes a method ShowEmployeeDetails to display the details of an employee.
  • The Developer and Manager classes are concrete implementations of employees with specific attributes such as employee ID, name, and position.
  • The CompanyDirectory class serves as a composite, representing a group of employees. It implements the IEmployee interface, allowing both individual employees and groups of employees to be treated uniformly. It can contain a list of IEmployee objects, which can be either individual employees or subgroups (other instances of CompanyDirectory).
  • The Company class acts as the client code, creating instances of individual employees (Developers and Managers) and organizing them into composite structures using CompanyDirectory. It then demonstrates the use of the Composite pattern by displaying the details of the entire employee structure

Below is the Code for the above problem statement:

C#




using System;
using System.Collections.Generic;
 
// A common interface for all employees
interface IEmployee
{
    void ShowEmployeeDetails();
}
 
class Developer : IEmployee
{
    private long empId;
    private string name;
    private string position;
 
    public Developer(long empId, string name, string position)
    {
        this.empId = empId;
        this.name = name;
        this.position = position;
    }
 
    public void ShowEmployeeDetails()
    {
        Console.WriteLine($"{empId} {name} {position}");
    }
}
 
class Manager : IEmployee
{
    private long empId;
    private string name;
    private string position;
 
    public Manager(long empId, string name, string position)
    {
        this.empId = empId;
        this.name = name;
        this.position = position;
    }
 
    public void ShowEmployeeDetails()
    {
        Console.WriteLine($"{empId} {name} {position}");
    }
}
 
// Class used to get Employee List
// and do the operations like
// add or remove Employee
class CompanyDirectory : IEmployee
{
    private List<IEmployee> employeeList = new List<IEmployee>();
 
    public void ShowEmployeeDetails()
    {
        foreach (IEmployee emp in employeeList)
        {
            emp.ShowEmployeeDetails();
        }
    }
 
    public void AddEmployee(IEmployee emp)
    {
        employeeList.Add(emp);
    }
 
    public void RemoveEmployee(IEmployee emp)
    {
        employeeList.Remove(emp);
    }
}
 
// Driver class
class Company
{
    public static void Main(string[] args)
    {
        Developer dev1 = new Developer(100, "Lokesh Sharma", "Pro Developer");
        Developer dev2 = new Developer(101, "Vinay Sharma", "Developer");
 
        CompanyDirectory engDirectory = new CompanyDirectory();
        engDirectory.AddEmployee(dev1);
        engDirectory.AddEmployee(dev2);
 
        Manager man1 = new Manager(200, "Kushagra Garg", "SEO Manager");
        Manager man2 = new Manager(201, "Vikram Sharma", "Kushagra's Manager");
 
        CompanyDirectory accDirectory = new CompanyDirectory();
        accDirectory.AddEmployee(man1);
        accDirectory.AddEmployee(man2);
 
        CompanyDirectory directory = new CompanyDirectory();
        directory.AddEmployee(engDirectory);
        directory.AddEmployee(accDirectory);
        directory.ShowEmployeeDetails();
    }
}


Output

100 Lokesh Sharma Pro Developer
101 Vinay Sharma Developer
200 Kushagra Garg SEO Manager
201 Vikram Sharma Kushagra's Manager






4.4 Decorator Method Design Patterns in C sharp (C#)

The Decorator pattern is a structural design pattern that allows behavior to be added to individual objects, either statically or dynamically, without affecting the behavior of other objects from the same class. It involves creating a set of decorator classes that are used to wrap concrete components. These decorators add new functionalities to the objects they decorate by providing alternative processing pathways.

Let’s understand Decorator Method by using an example:

Imagine a pizza ordering system, adding new toppings or combinations might involve modifying the existing pizza classes. It’s like having to change the core pizza recipe every time a new topping is introduced.

How Decorator Pattern gonna help to solve this problem:

Decorator Pattern allow customers to customize their pizza orders by adding various toppings dynamically without modifying the existing pizza classes. It’s like having a versatile system that adapts to various customer preferences, making the pizza customization process smooth and scalable.

In the below code:

  • An abstract class Pizza representing the base pizza, with concrete pizza classes such as PeppyPaneer, FarmHouse, etc.
  • There is an abstract decorator class ToppingsDecorator, extending Pizza, which represents additional toppings that can be added to the pizza.
  • Concrete topping classes like FreshTomato, Barbeque, and Paneer extend the ToppingsDecorator class.
  • The PizzaStore class serves as the driver program, creating instances of pizzas and decorating them with various toppings.

Below is the Code for the above problem statement:

C#




using System;
 
// Abstract Pizza class (All classes extend from this)
abstract class Pizza
{
    // it is an abstract pizza
    protected string description = "Unknown Pizza";
 
    public virtual string GetDescription()
    {
        return description;
    }
 
    public abstract int GetCost();
}
 
// The decorator class: It extends Pizza to be interchangeable with it toppings decorator can also be implemented as an interface
abstract class ToppingsDecorator : Pizza
{
    public abstract new string GetDescription();
}
 
// Concrete pizza classes
class PeppyPaneer : Pizza
{
    public PeppyPaneer() { description = "PeppyPaneer"; }
    public override int GetCost() { return 100; }
}
 
class FarmHouse : Pizza
{
    public FarmHouse() { description = "FarmHouse"; }
    public override int GetCost() { return 200; }
}
 
class Margherita : Pizza
{
    public Margherita() { description = "Margherita"; }
    public override int GetCost() { return 100; }
}
 
class ChickenFiesta : Pizza
{
    public ChickenFiesta() { description = "ChickenFiesta"; }
    public override int GetCost() { return 200; }
}
 
class SimplePizza : Pizza
{
    public SimplePizza() { description = "SimplePizza"; }
    public override int GetCost() { return 50; }
}
 
// Concrete toppings classes
class FreshTomato : ToppingsDecorator
{
    // we need a reference to obj we are decorating
    Pizza pizza;
 
    public FreshTomato(Pizza pizza) { this.pizza = pizza; }
    public override string GetDescription()
    {
        return pizza.GetDescription() + ", Fresh Tomato ";
    }
    public override int GetCost() { return 40 + pizza.GetCost(); }
}
 
class Barbeque : ToppingsDecorator
{
    Pizza pizza;
 
    public Barbeque(Pizza pizza) { this.pizza = pizza; }
    public override string GetDescription()
    {
        return pizza.GetDescription() + ", Barbeque ";
    }
    public override int GetCost()
    {
        // Check if the base pizza is null
        return pizza != null ? 90 + pizza.GetCost() : 90;
    }
}
 
class Paneer : ToppingsDecorator
{
    Pizza pizza;
 
    public Paneer(Pizza pizza) { this.pizza = pizza; }
    public override string GetDescription()
    {
        return pizza.GetDescription() + ", Paneer ";
    }
    public override int GetCost() { return 70 + pizza.GetCost(); }
}
 
// Other toppings can be coded in a similar way
 
// Driver class and method
class PizzaStore
{
    static void Main(string[] args)
    {
        // create new margherita pizza
        Pizza pizza = new Margherita();
        Console.WriteLine(pizza.GetDescription() +
                        " Cost :" + pizza.GetCost());
 
        // create new FarmHouse pizza
        Pizza pizza2 = new FarmHouse();
 
        // decorate it with freshtomato topping
        pizza2 = new FreshTomato(pizza2);
 
        // decorate it with paneer topping
        pizza2 = new Paneer(pizza2);
 
        Console.WriteLine(pizza2.GetDescription() +
                        " Cost :" + pizza2.GetCost());
 
        Pizza pizza3 = new Barbeque(null); // no specific pizza
        Console.WriteLine(pizza3.GetDescription() + " Cost :" + pizza3.GetCost());
    }
}


Output

Margherita Cost :100
Unknown Pizza Cost :310
Unknown Pizza Cost :90






4.5 Facade Method Design Patterns in C sharp (C#)

The Facade Pattern is a design pattern in software engineering that falls under the structural pattern category. It provides a simplified and unified interface to a set of interfaces or subsystems within a larger system, making it easier to use and reducing the complexity of the system for clients. Essentially, it acts as a facade or entry point to a more complex system, shielding clients from its intricacies.

Let’s understand Facade Method by using an example:

Lets consider a Car system, which includes an engine and lights, interacting with the car might involve dealing with the complexities of the engine and lights subsystems separately. It’s like having to understand the internal workings of these parts every time you want to start or stop the car.

How Facade Pattern gonna help to solve this problem:

The Facade Pattern hides the complexities of the engine and lights subsystems, offering a straightforward way to interact with the car. It’s like having a user-friendly control panel, making the car operation more accessible and user-friendly.

In the below code:

  • Created a subsystem for the engine with functionalities to start and stop the engine.
  • Developed another subsystem for lights with features to turn them on and off.
  • A Facade class named Car that encapsulates the interactions with the engine and lights subsystems.
  • The Car class have methods StartCar() to start the engine, turn on the lights, and indicate the car is ready to drive. Additionally, it should have StopCar() to turn off the lights, stop the engine, and indicate the car has stopped.
  • Demonstrated the usage of the Car class in the Main method, showcasing how the Facade pattern simplifies the process of starting and stopping the car without dealing with the complexities of individual subsystems.

Below is the Code for the above problem statement:

C#




using System;
 
// Subsystem 1
class Engine
{
    public void Start()
    {
        Console.WriteLine("Engine started");
    }
 
    public void Stop()
    {
        Console.WriteLine("Engine stopped");
    }
}
 
// Subsystem 2
class Lights
{
    public void TurnOn()
    {
        Console.WriteLine("Lights on");
    }
 
    public void TurnOff()
    {
        Console.WriteLine("Lights off");
    }
}
 
// Facade
class Car
{
    private Engine engine = new Engine();
    private Lights lights = new Lights();
 
    public void StartCar()
    {
        engine.Start();
        lights.TurnOn();
        Console.WriteLine("Car is ready to drive");
    }
 
    public void StopCar()
    {
        lights.TurnOff();
        engine.Stop();
        Console.WriteLine("Car has stopped");
    }
}
 
class Program
{
    static void Main()
    {
        // Using the Facade to start and stop the car
        Car car = new Car();
        car.StartCar();
        // Simulate some driving
        car.StopCar();
    }
}


Output

Engine started
Lights on
Car is ready to drive
Lights off
Engine stopped
Car has stopped






4.6 Flyweight Method Design Patterns in C sharp (C#)

Flyweight pattern is one of the structural design patterns as this pattern provides ways to decrease object count thus improving application required objects structure. Flyweight pattern is used when we need to create a large number of similar objects (say 105). One important feature of flyweight objects is that they are immutable. This means that they cannot be modified once they have been constructed.

Let’s understand Flyweight Method by using an example:

In the Counter-Strike game, There are be two types of players: Terrorists and Counter-Terrorists. Each player has a specific mission and can be equipped with different weapons but the problem is that there can be lot of instances for each player, duplicating mission details or weapon information. It’s like having the same information repeated multiple times, taking up a lot of memory.

How Flyweight Pattern gonna help to solve this problem:

The Flyweight pattern is used to optimize memory usage by sharing common intrinsic attributes among similar objects. For example the intrinsic attributes like the mission type (“PLANT A BOMB” for Terrorists and “DIFFUSE BOMB” for Counter-Terrorists). Extrinsic attributes, such as the weapon a player carries, are assigned dynamically.

In the below code:

  • Player Interface (IPlayer): Defines methods for assigning a weapon and performing a mission.
  • Concrete Player Classes (Terrorist and CounterTerrorist): Implement the IPlayer interface, having intrinsic attributes (mission type) and extrinsic attributes (assigned weapons).
  • Player Factory (PlayerFactory): Responsible for creating and managing player objects. Uses a Dictionary to store and retrieve players based on their type.
  • Driver Class (CounterStrike): Main class demonstrating the Flyweight pattern. Creates and assigns weapons to players in a Counter-Strike game simulation.

Below is the Code for the above problem statement:

C#




using System;
using System.Collections.Generic;
 
// Common interface for all players
interface IPlayer
{
    void AssignWeapon(string weapon);
    void Mission();
}
 
// Terrorist must have weapon and mission
class Terrorist : IPlayer
{
    // Intrinsic Attribute
    private const string TASK = "PLANT A BOMB";
 
    // Extrinsic Attribute
    private string weapon;
 
    public void AssignWeapon(string weapon)
    {
        // Assign a weapon
        this.weapon = weapon;
    }
 
    public void Mission()
    {
        // Work on the Mission
        Console.WriteLine($"Terrorist with weapon {weapon} | Task is {TASK}");
    }
}
 
// CounterTerrorist must have weapon and mission
class CounterTerrorist : IPlayer
{
    // Intrinsic Attribute
    private const string TASK = "DIFFUSE BOMB";
 
    // Extrinsic Attribute
    private string weapon;
 
    public void AssignWeapon(string weapon)
    {
        this.weapon = weapon;
    }
 
    public void Mission()
    {
        Console.WriteLine($"Counter Terrorist with weapon {weapon} | Task is {TASK}");
    }
}
 
// Class used to get a player using Dictionary (Returns
// an existing player if a player of given type exists.
// Else creates a new player and returns it.
class PlayerFactory
{
    // Dictionary stores the reference to the object
    // of Terrorist(TS) or CounterTerrorist(CT).
    private static Dictionary<string, IPlayer> playerDictionary =
                    new Dictionary<string, IPlayer>();
 
    // Method to get a player
    public static IPlayer GetPlayer(string type)
    {
        IPlayer player = null;
 
        // If an object for TS or CT has already been
        // created, simply return its reference
        if (playerDictionary.ContainsKey(type))
            player = playerDictionary[type];
        else
        {
            // Create an object of TS/CT
            switch (type)
            {
                case "Terrorist":
                    Console.WriteLine("Terrorist Created");
                    player = new Terrorist();
                    break;
                case "CounterTerrorist":
                    Console.WriteLine("Counter Terrorist Created");
                    player = new CounterTerrorist();
                    break;
                default:
                    Console.WriteLine("Unreachable code!");
                    break;
            }
 
            // Once created, insert it into the Dictionary
            playerDictionary.Add(type, player);
        }
        return player;
    }
}
 
// Driver class
class CounterStrike
{
    // All player types and weapons (used by GetRandPlayerType()
    // and GetRandWeapon()
    private static string[] playerType =
                    {"Terrorist", "CounterTerrorist"};
    private static string[] weapons =
    {"AK-47", "Maverick", "Gut Knife", "Desert Eagle"};
 
    // Driver code
    static void Main(string[] args)
    {
        // Assume that we have a total of 10 players
        for (int i = 0; i < 10; i++)
        {
            // GetPlayer() is called using the class name
            IPlayer player = PlayerFactory.GetPlayer(GetRandPlayerType());
 
            // Assign a weapon chosen randomly uniformly
            // from the weapons array
            player.AssignWeapon(GetRandWeapon());
 
            // Send this player on a mission
            player.Mission();
        }
    }
 
    // Utility methods to get a random player type and
    // weapon
    static string GetRandPlayerType()
    {
        Random random = new Random();
 
        // Will return an integer between [0,2)
        int randInt = random.Next(playerType.Length);
 
        // Return the player stored at index 'randInt'
        return playerType[randInt];
    }
 
    static string GetRandWeapon()
    {
        Random random = new Random();
 
        // Will return an integer between [0,5)
        int randInt = random.Next(weapons.Length);
 
        // Return the weapon stored at index 'randInt'
        return weapons[randInt];
    }
}


Output

Counter Terrorist Created
Counter Terrorist with weapon Desert Eagle | Task is DIFFUSE BOMB
Counter Terrorist with weapon AK-47 | Task is DIFFUSE BOMB
Counter Terrorist with weapon Gut Knife | Task is...





4.7. Proxy Method Design Patterns in C sharp (C#)

A Proxy is a design pattern that allows one object to stand in for another. It acts as a substitute or placeholder, controlling access to the real object. With a Proxy, you can execute actions before or after a request reaches the actual object, giving you the ability to manage and modify the behavior of the real object transparently.

Let’s understand Proxy Method by using an example:

Imagine a system of typical banking operations such as depositing money, withdrawing money, and checking the account balance. Now, these operations involve sensitive data, and we want to make sure they are secure. If we directly access these operations, there’s a risk of unauthorized access or tampering., So these operations should be accessible only after successful authentication.

How Proxy Pattern gonna help to solve this problem:

Proxy Pattern acts as a protective barrier between you and the sensitive operations. Before you can deposit, withdraw, or check balance, the proxy ensures there’s proper authentication.

In the below code:

  • The code we will design should defines two classes, RealBankAccount representing an actual bank account, and SecureBankAccountProxy acting as a proxy that controls access to the real bank account.
  • The proxy requires a password for authentication, and if the password is correct, it allows operations like depositing money, withdrawing money, and checking the account balance.
  • The client code demonstrates using the proxy to perform these operations on the bank account.

Below is the Code for the above problem statement:

C#




using System;
 
// Subject interface
interface IBankAccount
{
    void Deposit(double amount);
    void Withdraw(double amount);
    double GetBalance();
}
 
// RealSubject class
class RealBankAccount : IBankAccount
{
    private double balance;
 
    public void Deposit(double amount)
    {
        balance += amount;
        Console.WriteLine($"Deposited: {amount}");
    }
 
    public void Withdraw(double amount)
    {
        if (balance >= amount)
        {
            balance -= amount;
            Console.WriteLine($"Withdrawn: {amount}");
        }
        else
        {
            Console.WriteLine("Insufficient funds!");
        }
    }
 
    public double GetBalance()
    {
        return balance;
    }
}
 
// Proxy class
class SecureBankAccountProxy : IBankAccount
{
    private RealBankAccount realBankAccount;
    private string password;
 
    public SecureBankAccountProxy(string password)
    {
        this.password = password;
        Authenticate();
    }
 
    private void Authenticate()
    {
        if (password == "secret")
        {
            realBankAccount = new RealBankAccount();
            Console.WriteLine("Authentication successful.");
        }
        else
        {
            Console.WriteLine("Authentication failed. Access denied.");
        }
    }
 
    public void Deposit(double amount)
    {
        if (realBankAccount != null)
        {
            realBankAccount.Deposit(amount);
        }
    }
 
    public void Withdraw(double amount)
    {
        if (realBankAccount != null)
        {
            realBankAccount.Withdraw(amount);
        }
    }
 
    public double GetBalance()
    {
        return (realBankAccount != null) ? realBankAccount.GetBalance() : 0.0;
    }
}
 
// Client code
class ProxyBankExample
{
    static void Main(string[] args)
    {
        // Using the proxy to access the real bank account
        IBankAccount account = new SecureBankAccountProxy("secret");
 
        // Operations
        account.Deposit(1000);
        account.Withdraw(500);
        double balance = account.GetBalance();
 
        Console.WriteLine($"Current Balance: {balance}");
    }
}


Output

Authentication successful.
Deposited: 1000
Withdrawn: 500
Current Balance: 500






5. Behavioral Design Patterns in C sharp (C#)

Behavioral Patterns are concerned with algorithms and the assignment of responsibilities between objects. Behavioral patterns describe not just patterns of objects or classes but also the patterns of communication between them. These patterns characterize complex control flow that’s difficult to follow at run-time.

There are three recurring themes in these patterns:

  • Behavioral class patterns use inheritance to distribute behavior between classes. 
  • Behavioral object patterns use object composition rather than inheritance.
  • Behavioral object patterns are concerned with encapsulating behavior in an object and delegating requests to it. 

5.1. Chain of Responsibility Method Design Pattern in C sharp (C#)

Chain of responsibility pattern is used to achieve loose coupling in software design where a request from the client is passed to a chain of objects to process them. Later, the object in the chain will decide themselves who will be processing the request and whether the request is required to be sent to the next object in the chain or not.

Let’s understand Chain of Responsibilty Method by using an example:

When designing a chain of processor (which handles the request) , we have to manage the flow of requests, Each processor might need explicit dependencies on subsequent processors, making it challenging to modify or extend the processing logic and could result in a rigid and less maintainable code structure, How we can make a system in which there is efficiently handled related tasks with a clear structure, enabling each processor to decide if it can address a request or delegate it to the next processor in the chain.

How Chain of Responsibilty Pattern gonna help to solve this problem:

Using the Chain of Responsibility design pattern, a flexible and extensible system is designed to handle numeric operations. Multiple processors form a chain, each capable of independently deciding whether to handle a numeric request or pass it to the next processor in the chain.

In the below code:

  • Processors: Processors (NegativeProcessor, ZeroProcessor, PositiveProcessor) handle requests based on being negative, zero, or positive.
  • Number Class: Number class represents the request with an integer value.
  • Chain Creation: The Chain class initializes a chain of processors during creation.
  • Processing Requests: Each processor decides to handle or pass the request to the next processor.
  • Client (TestChain): Demonstrates creating a chain and passing numbers through for processing.
  • Outputs which processor handles each type of number in the chain.

Below is the Code for the above problem statement:

C#




using System;
 
public class Chain
{
    private Processor chain;
 
    public Chain()
    {
        BuildChain();
    }
 
    private void BuildChain()
    {
        chain = new NegativeProcessor(new ZeroProcessor(new PositiveProcessor(null)));
    }
 
    public void Process(Number request)
    {
        chain.Process(request);
    }
}
 
public abstract class Processor
{
    private readonly Processor nextProcessor;
 
    public Processor(Processor nextProcessor)
    {
        this.nextProcessor = nextProcessor;
    }
 
    public virtual void Process(Number request)
    {
        if (nextProcessor != null)
            nextProcessor.Process(request);
    }
}
 
public class Number
{
    private readonly int number;
 
    public Number(int number)
    {
        this.number = number;
    }
 
    public int GetNumber()
    {
        return number;
    }
}
 
public class NegativeProcessor : Processor
{
    public NegativeProcessor(Processor nextProcessor) : base(nextProcessor)
    {
    }
 
    public override void Process(Number request)
    {
        if (request.GetNumber() < 0)
        {
            Console.WriteLine("NegativeProcessor : " + request.GetNumber());
        }
        else
        {
            base.Process(request);
        }
    }
}
 
public class ZeroProcessor : Processor
{
    public ZeroProcessor(Processor nextProcessor) : base(nextProcessor)
    {
    }
 
    public override void Process(Number request)
    {
        if (request.GetNumber() == 0)
        {
            Console.WriteLine("ZeroProcessor : " + request.GetNumber());
        }
        else
        {
            base.Process(request);
        }
    }
}
 
public class PositiveProcessor : Processor
{
    public PositiveProcessor(Processor nextProcessor) : base(nextProcessor)
    {
    }
 
    public override void Process(Number request)
    {
        if (request.GetNumber() > 0)
        {
            Console.WriteLine("PositiveProcessor : " + request.GetNumber());
        }
        else
        {
            base.Process(request);
        }
    }
}
 
class TestChain
{
    public static void Main(string[] args)
    {
        Chain chain = new Chain();
 
        // Calling chain of responsibility
        chain.Process(new Number(90));
        chain.Process(new Number(-50));
        chain.Process(new Number(0));
        chain.Process(new Number(91));
    }
}


Output

PositiveProcessor : 90
NegativeProcessor : -50
ZeroProcessor : 0
PositiveProcessor : 91






5.2.Command Method Design Pattern in C sharp (C#)

The command pattern encapsulates a request as an object, thereby letting us parameterize other objects with different requests, queue or log requests, and support undoable operations.

Let’s understand Command Method by using an example:

When communication between the sender and the receiver, sender (remote control) would need to directly interact with the receiver (light, stereo) to execute commands. This tight coupling makes it challenging to introduce new commands or modify existing ones without affecting the sender.

How Command Pattern gonna help to solve this problem:

The Command Pattern encapsulates each command as an object. This decouples the sender from the specific operations of the receiver. The remote control only needs to know how to execute a generic command interface, and new commands can be easily added without modifying the sender.

In the below code:

  • Remote Control Setup: There’s a simple remote control (SimpleRemoteControl) with a single button. The remote control is designed to work with any command that implements the ICommand interface.
  • Commands: Commands are represented by classes that implement the ICommand interface. Example commands include turning on a light (LightOnCommand), turning off a light (LightOffCommand), operating a stereo with a CD (StereoOnWithCDCommand), and turning off a stereo (StereoOffCommand).
  • Command Execution: The remote control can be dynamically configured with different commands. When you press the button on the remote control, it executes the associated command.

Below is the Code for the above problem statement:

C#




using System;
 
// Command interface
interface ICommand
{
    void Execute();
}
 
// Light class and its corresponding command classes
class Light
{
    public void On()
    {
        Console.WriteLine("Light is on");
    }
 
    public void Off()
    {
        Console.WriteLine("Light is off");
    }
}
 
class LightOnCommand : ICommand
{
    private readonly Light _light;
 
    public LightOnCommand(Light light)
    {
        _light = light;
    }
 
    public void Execute()
    {
        _light.On();
    }
}
 
class LightOffCommand : ICommand
{
    private readonly Light _light;
 
    public LightOffCommand(Light light)
    {
        _light = light;
    }
 
    public void Execute()
    {
        _light.Off();
    }
}
 
// Stereo and its command classes
class Stereo
{
    public void On()
    {
        Console.WriteLine("Stereo is on");
    }
 
    public void Off()
    {
        Console.WriteLine("Stereo is off");
    }
 
    public void SetCD()
    {
        Console.WriteLine("Stereo is set for CD input");
    }
 
    public void SetVolume(int volume)
    {
        Console.WriteLine($"Stereo volume set to {volume}");
    }
}
 
class StereoOffCommand : ICommand
{
    private readonly Stereo _stereo;
 
    public StereoOffCommand(Stereo stereo)
    {
        _stereo = stereo;
    }
 
    public void Execute()
    {
        _stereo.Off();
    }
}
 
class StereoOnWithCDCommand : ICommand
{
    private readonly Stereo _stereo;
 
    public StereoOnWithCDCommand(Stereo stereo)
    {
        _stereo = stereo;
    }
 
    public void Execute()
    {
        _stereo.On();
        _stereo.SetCD();
        _stereo.SetVolume(11);
    }
}
 
// Simple remote control with one button
class SimpleRemoteControl
{
    private ICommand _slot; // only one button
 
    public SimpleRemoteControl()
    {
    }
 
    public void SetCommand(ICommand command)
    {
        // set the command the remote will execute
        _slot = command;
    }
 
    public void ButtonWasPressed()
    {
        _slot.Execute();
    }
}
 
// Driver class
class RemoteControlTest
{
    static void Main(string[] args)
    {
        SimpleRemoteControl remote = new SimpleRemoteControl();
        Light light = new Light();
        Stereo stereo = new Stereo();
 
        // We can change command dynamically
        remote.SetCommand(new LightOnCommand(light));
        remote.ButtonWasPressed();
        remote.SetCommand(new StereoOnWithCDCommand(stereo));
        remote.ButtonWasPressed();
        remote.SetCommand(new StereoOffCommand(stereo));
        remote.ButtonWasPressed();
    }
}


Output

Light is on
Stereo is on
Stereo is set for CD input
Stereo volume set to 11
Stereo is off






5.3. Observer Method Design Pattern in C sharp (C#)

The Observer Pattern defines a one to many dependency between objects so that one object changes state, all of its dependents are notified and updated automatically.

  • One to many dependency is between Subject(One) and Observer(Many).
  • There is dependency as Observers themselves don’t have access to data. They are dependent on Subject to provide them data.

Let’s understand Observer Method by using an example:

Lets consider a weather monitoring system where multiple displays need to show real-time updates whenever there is a change in weather conditions but the challenge is to maintain consistency across observer and also ensuring that observers don’t negatively impact each other or the subject or potential performance concerns if there are many observers or frequent updates.

How Observer Pattern gonna help to solve this problem:

Using Observer pattern helps to make a flexible and loosely coupled system where new displays can be added without modifying the WeatherStation code.

In the below code:

The WeatherStation acts as the subject, and DisplayDevice instances act as observers.

  • Displays can be dynamically added or removed from observation.
  • When the weather changes (SetWeather is called), all registered displays are automatically updated (NotifyObservers).

Below is the Code for the above problem statement:

C#




using System;
using System.Collections.Generic;
 
// Subject (WeatherStation)
public class WeatherStation
{
    private List<IObserver> observers = new List<IObserver>();
    private string currentWeather;
 
    public void AddObserver(IObserver observer)
    {
        observers.Add(observer);
    }
 
    public void RemoveObserver(IObserver observer)
    {
        observers.Remove(observer);
    }
 
    public void NotifyObservers()
    {
        foreach (var observer in observers)
        {
            observer.Update(currentWeather);
        }
    }
 
    public void SetWeather(string newWeather)
    {
        currentWeather = newWeather;
        NotifyObservers();
    }
}
 
// Observer Interface
public interface IObserver
{
    void Update(string weather);
}
 
// Concrete Observer (DisplayDevice)
public class DisplayDevice : IObserver
{
    private string displayLocation;
 
    public DisplayDevice(string location)
    {
        displayLocation = location;
    }
 
    public void Update(string weather)
    {
        Console.WriteLine($"Display at {displayLocation} updated. Current weather: {weather}");
    }
}
 
// Client Code
class Program
{
    static void Main()
    {
        WeatherStation weatherStation = new WeatherStation();
 
        DisplayDevice mobileApp = new DisplayDevice("Mobile App");
        DisplayDevice website = new DisplayDevice("Website");
        DisplayDevice dedicatedDevice = new DisplayDevice("Dedicated Device");
 
        weatherStation.AddObserver(mobileApp);
        weatherStation.AddObserver(website);
        weatherStation.AddObserver(dedicatedDevice);
 
        weatherStation.SetWeather("Sunny"); // All displays should update
        weatherStation.SetWeather("Rainy"); // All displays should update
    }
}


Output

Display at Mobile App updated. Current weather: Sunny
Display at Website updated. Current weather: Sunny
Display at Dedicated Device updated. Current weather: Sunny
Display at Mobile App updated. Curr...





5.4 State Method Design Pattern in C sharp (C#)

The state pattern is one of the behavioral design patterns. A state design pattern is used when an Object changes its behavior based on its internal state. If we have to change the behavior of an object based on its state, we can have a state variable in the Object and use the if-else condition block to perform different actions based on the state. The state pattern is used to provide a systematic and lose-coupled way to achieve this through Context and State implementations.

Let’s understand State Method by using an example:

Lets consider a document editing application, it can have different states such as Draft, Review, and Published. The challenges here could be managing transitions between states and ensuring that actions are appropriately handled based on the current state.

How State Pattern gonna help to solve this problem:

The State design pattern is that it allows an object to alter its behavior when its internal state changes. This pattern encapsulates states into separate classes, and the object delegates the state-specific behavior to these classes.

In the below code:

Document class represents the context that can be in different states (DraftState, ReviewState, PublishedState).

  • The Handle method of each state is called based on the current state, and it performs actions accordingly.
  • The ChangeState method is used to transition between states.
  • This pattern allows the context to behave differently based on its internal state, making it more flexible and maintainable.

Below is the Code for the above problem statement:

C#




using System;
 
// State interface
public interface IState
{
    void Handle();
}
 
// Concrete States
public class DraftState : IState
{
    public void Handle()
    {
        Console.WriteLine("Document is in Draft state. Editing is allowed.");
    }
}
 
public class ReviewState : IState
{
    public void Handle()
    {
        Console.WriteLine("Document is in Review state. Editing is restricted. Reviewers can provide feedback.");
    }
}
 
public class PublishedState : IState
{
    public void Handle()
    {
        Console.WriteLine("Document is in Published state. Editing is not allowed. It is publicly available.");
    }
}
 
// Context
public class Document
{
    private IState currentState;
 
    public Document()
    {
        currentState = new DraftState(); // Initial state is Draft
    }
 
    public void ChangeState(IState newState)
    {
        currentState = newState;
    }
 
    public void Request()
    {
        currentState.Handle();
    }
}
 
// Client Code
class Program
{
    static void Main()
    {
        Document document = new Document();
 
        document.Request(); // Output: Document is in Draft state. Editing is allowed.
 
        document.ChangeState(new ReviewState());
        document.Request(); // Output: Document is in Review state. Editing is restricted. Reviewers can provide feedback.
 
        document.ChangeState(new PublishedState());
        document.Request(); // Output: Document is in Published state. Editing is not allowed. It is publicly available.
    }
}


Output

Document is in Draft state. Editing is allowed.
Document is in Review state. Editing is restricted. Reviewers can provide feedback.
Document is in Published state. Editing is not allowed. It is public...





5.5. Strategy Method Design Pattern in C sharp (C#)

Strategy pattern is a behavioral design pattern that allows the behavior of an object to be selected at runtime. It is one of the Gang of Four (GoF) design patterns, which are widely used in object-oriented programming. The Strategy pattern is based on the idea of encapsulating a family of algorithms into separate classes that implement a common interface.

Let’s understand Strategy Method by using an example:

Lets consider an e-commerce platform, and we need to handle payments. Different payment gateways (algorithms) such as PayPal, Credit Card, and Bitcoin may be used interchangeably based on user preferences but the challenge is when we handle payment processing directly within the client code, we might end up with hardcoded dependencies on specific payment methods and can leads to code redundancy and maintenance challenges.

How Strategy Pattern gonna help to solve this problem:

By applying the Strategy Pattern, you can address these challenges by encapsulating each payment method in a separate strategy class. This promotes code reuse, modularity, and easier extensibility without modifying existing code. It also allows for dynamic switching between payment strategies, making the system more flexible and scalable.

In the below code:

  • PaymentProcessor Class: This is the context class that holds a reference to the current payment strategy. It has a method ProcessPayment that delegates the payment processing to the strategy.
  • PaymentStrategy Interface: The interface declares a method ProcessPayment that concrete strategies must implement.
  • Concrete Payment Strategies: Classes like CreditCardPayment and PayPalPayment implement the PaymentStrategy interface, providing their specific implementation of payment processing.
  • Client Code (Program class): The client creates an instance of the PaymentProcessor and sets its payment strategy dynamically by calling SetPaymentStrategy. This allows the client to switch between different payment methods seamlessly.

Below is the Code for the above problem statement:

C#




using System;
 
// Strategy Interface
public interface IPaymentStrategy
{
    void ProcessPayment(float amount);
}
 
// Concrete Strategies
public class PayPalPayment : IPaymentStrategy
{
    public void ProcessPayment(float amount)
    {
        Console.WriteLine($"Processing PayPal payment of {amount} dollars.");
        // Additional PayPal-specific logic
    }
}
 
public class CreditCardPayment : IPaymentStrategy
{
    public void ProcessPayment(float amount)
    {
        Console.WriteLine($"Processing Credit Card payment of {amount} dollars.");
        // Additional Credit Card-specific logic
    }
}
 
public class BitcoinPayment : IPaymentStrategy
{
    public void ProcessPayment(float amount)
    {
        Console.WriteLine($"Processing Bitcoin payment of {amount} dollars.");
        // Additional Bitcoin-specific logic
    }
}
 
// Context
public class PaymentContext
{
    private IPaymentStrategy paymentStrategy;
 
    public PaymentContext(IPaymentStrategy strategy)
    {
        this.paymentStrategy = strategy;
    }
 
    public void SetPaymentStrategy(IPaymentStrategy strategy)
    {
        this.paymentStrategy = strategy;
    }
 
    public void ProcessPayment(float amount)
    {
        paymentStrategy.ProcessPayment(amount);
    }
}
 
// Client Code
class Program
{
    static void Main()
    {
        PaymentContext paymentContext = new PaymentContext(new PayPalPayment());
         
        paymentContext.ProcessPayment(100.0f);
 
        // Switching the payment method at runtime
        paymentContext.SetPaymentStrategy(new BitcoinPayment());
        paymentContext.ProcessPayment(50.0f);
    }
}


Output

Processing PayPal payment of 100 dollars.
Processing Bitcoin payment of 50 dollars.






5.6. Template Method Design Pattern in C sharp (C#)

Template method design pattern is to define an algorithm as a skeleton of operations and leave the details to be implemented by the child classes. The overall structure and sequence of the algorithm are preserved by the parent class.

Let’s understand Template Method by using an example:

Lets consider an application where various types of reports need to be generated (e.g., PDF report, Excel report). While the overall structure of report generation is similar, the details (like data fetching, formatting) vary for each type of report but there can be challenges like duplicating the common steps across different report classes, leading to code redundancy. If a change is needed in the common structure, you’d have to modify each report class separately, violating the DRY (Don’t Repeat Yourself) principle.

How Template Pattern gonna help to solve this problem:

This Template Method pattern ensures that the overall algorithm’s structure remains consistent across different implementations, allowing for code reuse, maintenance, and extensibility.

In the below code:

  • The ReportTemplate abstract class provides a template method, GenerateReport, defining the overall structure of a report.
  • Concrete subclasses like PdfReport and ExcelReport implement specific steps (FetchData and FormatData) according to their report types.
  • The common method DisplayReport ensures consistent behavior. In the Main method, instances of PdfReport and ExcelReport are created, and the GenerateReport method is invoked

Below is the Code for the above problem statement:

C#




using System;
 
abstract class ReportTemplate
{
    // Template method defining the overall structure
    public void GenerateReport()
    {
        Console.WriteLine("Generating Report Header");
        FetchData();
        FormatData();
        DisplayReport();
    }
 
    // Abstract methods to be implemented by concrete classes
    protected abstract void FetchData();
    protected abstract void FormatData();
 
    // Common implementation
    private void DisplayReport()
    {
        Console.WriteLine("Displaying Report");
    }
}
 
class PdfReport : ReportTemplate
{
    // Concrete implementation for PDF report
    protected override void FetchData()
    {
        Console.WriteLine("Fetching Data for PDF Report");
    }
 
    protected override void FormatData()
    {
        Console.WriteLine("Formatting Data for PDF Report");
    }
}
 
class ExcelReport : ReportTemplate
{
    // Concrete implementation for Excel report
    protected override void FetchData()
    {
        Console.WriteLine("Fetching Data for Excel Report");
    }
 
    protected override void FormatData()
    {
        Console.WriteLine("Formatting Data for Excel Report");
    }
}
 
class Program
{
    static void Main()
    {
        ReportTemplate pdfReport = new PdfReport();
        pdfReport.GenerateReport();
 
        ReportTemplate excelReport = new ExcelReport();
        excelReport.GenerateReport();
    }
}


Output

Generating Report Header
Fetching Data for PDF Report
Formatting Data for PDF Report
Displaying Report
Generating Report Header
Fetching Data for Excel Report
Formatting Data for Excel Report
Displayi...





5.7. Visitor Method Design Pattern in C sharp (C#)

The Visitor pattern allows you to define new operations on an object structure without changing the classes of the elements on which it operates. The separation of concerns between the visitor and the elements being visited promotes extensibility and maintenance of the codebase.

Let’s understand Visitor Method by using an example:

Imagine we have a document structure with different elements like Paragraph and Table, and we want to perform operations like rendering and counting words on these elements , when we try modify each document element class whenever a new operation is introduced, this can lead to code modification and potential code duplication, violating the Open/Closed Principle.

How Visitor Pattern gonna help to solve this problem:

The Visitor pattern addresses these challenges by separating the operations into visitor classes. New operations can be added by introducing new visitor classes without modifying the existing element classes, promoting a more modular and extensible design.

In the below code:

  • Paragraph and Table classes implementing the IDocumentElement interface, and two visitors (RenderVisitor and WordCountVisitor) implementing the IVisitor interface.
  • The client code can apply different visitors to elements without modifying the element classes.

Below is the Code for the above problem statement:

C#




using System;
using System.Collections.Generic;
 
// Element interface
interface IDocumentElement
{
    void Accept(IVisitor visitor);
}
 
// Concrete element 1: Paragraph
class Paragraph : IDocumentElement
{
    public void Accept(IVisitor visitor)
    {
        visitor.VisitParagraph(this);
    }
}
 
// Concrete element 2: Table
class Table : IDocumentElement
{
    public void Accept(IVisitor visitor)
    {
        visitor.VisitTable(this);
    }
}
 
// Visitor interface
interface IVisitor
{
    void VisitParagraph(Paragraph paragraph);
    void VisitTable(Table table);
}
 
// Concrete visitor 1: RenderVisitor
class RenderVisitor : IVisitor
{
    public void VisitParagraph(Paragraph paragraph)
    {
        Console.WriteLine("Rendering Paragraph");
    }
 
    public void VisitTable(Table table)
    {
        Console.WriteLine("Rendering Table");
    }
}
 
// Concrete visitor 2: WordCountVisitor
class WordCountVisitor : IVisitor
{
    public void VisitParagraph(Paragraph paragraph)
    {
        Console.WriteLine("Counting words in Paragraph");
    }
 
    public void VisitTable(Table table)
    {
        Console.WriteLine("Counting words in Table");
    }
}
 
// Client code
class Client
{
    static void Main()
    {
        List<IDocumentElement> elements = new List<IDocumentElement>
        {
            new Paragraph(),
            new Table()
        };
 
        IVisitor renderVisitor = new RenderVisitor();
        IVisitor wordCountVisitor = new WordCountVisitor();
 
        foreach (var element in elements)
        {
            element.Accept(renderVisitor);
            element.Accept(wordCountVisitor);
        }
    }
}


Output

Rendering Paragraph
Counting words in Paragraph
Rendering Table
Counting words in Table






5.8. Mediator design pattern in C sharp (C#)

Mediator pattern enables decoupling of objects by introducing a layer in between so that the interaction between objects happen via the layer. If the objects interact with each other directly, the system components are tightly-coupled with each other that makes higher maintainability cost and not hard to extend. Mediator pattern focuses on providing a mediator between objects for communication and help in implementing loose-coupling between objects

Let’s understand Mediator Method by using an example:

Lets consider Chat application where users can send messages to each other. Direct communication between users would lead to a highly coupled system. Adding or removing users, or changing the way communication works, could become complex and error-prone.

How Mediator Pattern gonna help to solve this problem:

The mediator acts as a centralized hub that handles the communication between users. Each user communicates only with the mediator, and the mediator then relays messages to the appropriate recipients.

In below code:

The ChatMediator acts as a mediator between users (User objects), allowing them to send and receive messages without knowing the details of each other.

Below is the Code for the above problem statement:

C#




using System;
using System.Collections.Generic;
 
// Mediator Interface
public interface IChatMediator
{
    void SendMessage(User sender, string message);
}
 
// Concrete Mediator
public class ChatMediator : IChatMediator
{
    private List<User> users = new List<User>();
 
    public void AddUser(User user)
    {
        users.Add(user);
    }
 
    public void SendMessage(User sender, string message)
    {
        foreach (var user in users)
        {
            // Send message to all users except the sender
            if (user != sender)
                user.ReceiveMessage(message);
        }
    }
}
 
// Colleague Class
public class User
{
    private IChatMediator mediator;
    public string Name { get; }
 
    public User(IChatMediator mediator, string name)
    {
        this.mediator = mediator;
        Name = name;
        mediator.AddUser(this);
    }
 
    public void SendMessage(string message)
    {
        Console.WriteLine($"{Name} sends: {message}");
        mediator.SendMessage(this, message);
    }
 
    public void ReceiveMessage(string message)
    {
        Console.WriteLine($"{Name} receives: {message}");
    }
}
 
// Client Code
class Program
{
    static void Main()
    {
        IChatMediator mediator = new ChatMediator();
 
        User user1 = new User(mediator, "Alice");
        User user2 = new User(mediator, "Bob");
        User user3 = new User(mediator, "Charlie");
 
        user1.SendMessage("Hello, everyone!");
    }
}


5.9. Memento Design Pattern in C sharp (C#)

Memento pattern is a behavioral design pattern. Memento pattern is used to restore state of an object to a previous state. Intent of Memento Design pattern is without violating encapsulation, capture and externalize an object’s internal state so that the object can be restored to this state later.

Let’s understand Memento Method by using an example:

Imagine a a text editor application where users can undo and redo their actions. Implementing undo and redo functionality could become complex, storing and managing the state history directly within the text editor would lead to tight coupling and increased code complexity. Each action might need custom code to handle its own undo and redo logic, making the system less maintainable.

How Memento Pattern gonna help to solve this problem:

The memento design pattern can be used to implement the undo and redo functionality. Each time a user performs an action, a snapshot (memento) of the editor’s state is created and stored. Users can then undo or redo their actions by reverting to these saved states.

In below code:

  • The TextEditor is the originator that has the content, and the History class acts as the caretaker, storing and managing the states (mementos).
  • Users can undo changes by restoring the editor’s state from the history.

Below is the Code for the above problem statement:

C#




using System;
using System.Collections.Generic;
 
// Memento
public class EditorMemento
{
    public string Content { get; }
 
    public EditorMemento(string content)
    {
        Content = content;
    }
}
 
// Originator
public class TextEditor
{
    private string content;
 
    public string Content
    {
        get => content;
        set
        {
            content = value;
            Console.WriteLine($"Current Content: {content}");
        }
    }
 
    public EditorMemento Save()
    {
        return new EditorMemento(content);
    }
 
    public void Restore(EditorMemento memento)
    {
        content = memento.Content;
        Console.WriteLine($"Restored Content: {content}");
    }
}
 
// Caretaker
public class History
{
    private List<EditorMemento> states = new List<EditorMemento>();
 
    public void SaveState(EditorMemento memento)
    {
        states.Add(memento);
    }
 
    public EditorMemento GetLastState()
    {
        if (states.Count == 0)
            return null;
 
        var lastState = states[^1];
        states.RemoveAt(states.Count - 1);
        return lastState;
    }
}
 
// Client Code
class Program
{
    static void Main()
    {
        TextEditor editor = new TextEditor();
        History history = new History();
 
        // Initial content
        editor.Content = "Hello, ";
 
        // Save state
        history.SaveState(editor.Save());
 
        // Make changes
        editor.Content += "world!";
 
        // Save state
        history.SaveState(editor.Save());
 
        // Undo
        editor.Restore(history.GetLastState());
    }
}


5.10. Interpreter Design Pattern in C sharp (C#)

Interpreter design pattern is one of the behavioral design pattern. Interpreter pattern is used to defines a grammatical representation for a language and provides an interpreter to deal with this grammar. This pattern involves implementing an expression interface which tells to interpret a particular context.This pattern performs upon a hierarchy of expressions. Each expression here is a terminal or non-terminal. 

  • The tree structure of Interpreter design pattern is somewhat similar to that defined by the composite design pattern with terminal expressions being leaf objects and non-terminal expressions being composites. 
  • The tree contains the expressions to be evaluated and is usually generated by a parser. The parser itself is not a part of the interpreter pattern.

Let’s understand Interpreter Method by using an example:

Imagine you’re building a simple programming language, and you want to interpret and execute mathematical expressions written in that language. It might need to implement complex and hard-to-maintain code directly in the client program to parse and evaluate expressions. This could lead to code duplication and make it challenging to extend or modify the language grammar.

How Interpreter Pattern gonna help to solve this problem:

The Interpreter pattern is used to create a simple arithmetic expression language, allowing you to interpret and evaluate expressions.

In below code:

  • Expression: There’s an abstract class representing expressions in the language. There are two types of expressions – one for numbers (NumberExpression) and another for addition (AdditionExpression).
  • NumberExpression: Represents a numeric value in the expression.
  • AdditionExpression: Represents the addition operation in the expression, combining two sub-expressions.
  • Client Code (Program): The client creates a specific expression, like “1 + 2”, using instances of NumberExpression and AdditionExpression. Then, it interprets and evaluates the expression to get the result, which is printed to the console.

Below is the Code for the above problem statement:

C#




using System;
 
// Abstract expression class
abstract class Expression
{
    public abstract int Interpret();
}
 
// Terminal expression class
class NumberExpression : Expression
{
    private int number;
 
    public NumberExpression(int number)
    {
        this.number = number;
    }
 
    public override int Interpret()
    {
        return number;
    }
}
 
// Non-terminal expression class
class AdditionExpression : Expression
{
    private Expression left;
    private Expression right;
 
    public AdditionExpression(Expression left, Expression right)
    {
        this.left = left;
        this.right = right;
    }
 
    public override int Interpret()
    {
        return left.Interpret() + right.Interpret();
    }
}
 
// Client code
class Program
{
    static void Main()
    {
        // Creating expressions: 1 + 2
        Expression expression = new AdditionExpression(new NumberExpression(1), new NumberExpression(2));
 
        // Interpreting and evaluating the expression
        int result = expression.Interpret();
        Console.WriteLine("Result: " + result); // Output: 3
    }
}


Output

Result: 3








Like Article
Suggest improvement
Next
Share your thoughts in the comments

Similar Reads