TechieClues TechieClues
Updated date Apr 05, 2023
In this article, we will explore some of the most commonly used design patterns in C# .NET, along with sample code and explanations.

Introduction:

Design patterns are a set of solutions that are designed to solve common programming problems. They provide a standardized approach to design, making it easier for developers to create maintainable and scalable software systems. In C# .NET, there are a variety of design patterns that developers can use to improve the structure and quality of their code. In this article, we will explore some of the most commonly used design patterns in C# .NET, along with sample code and explanations.

1. Singleton Pattern

The Singleton pattern is used to ensure that a class has only one instance and provides a global point of access to that instance. This pattern is commonly used for objects that need to be shared across multiple parts of the application.

Example:

public class Singleton
{
    private static Singleton instance = null;
    private Singleton() { }

    public static Singleton Instance
    {
        get
        {
            if (instance == null)
            {
                instance = new Singleton();
            }
            return instance;
        }
    }

    public void DoSomething()
    {
        // implementation
    }
}

In the above code, the Singleton class has a private constructor and a private static instance variable. The public static property Instance provides the only way to access the Singleton instance. The constructor is private to prevent any other code from creating new instances of the Singleton class. The DoSomething() method is just a sample method in the Singleton class.

2. Factory Pattern

The Factory pattern is used to create objects without exposing the object creation logic to the client. This pattern is used to abstract the object creation process and allows the client to focus on using the objects rather than creating them.

Example:

public abstract class Vehicle
{
    public abstract void Drive();
}

public class Car : Vehicle
{
    public override void Drive()
    {
        Console.WriteLine("Driving a car.");
    }
}

public class Truck : Vehicle
{
    public override void Drive()
    {
        Console.WriteLine("Driving a truck.");
    }
}

public class VehicleFactory
{
    public Vehicle CreateVehicle(string vehicleType)
    {
        switch (vehicleType)
        {
            case "Car":
                return new Car();
            case "Truck":
                return new Truck();
            default:
                throw new Exception("Invalid vehicle type.");
        }
    }
}

In the above code, the Vehicle class is an abstract class that defines the basic structure of a vehicle. The Car and Truck classes are concrete implementations of the Vehicle class. The VehicleFactory class is responsible for creating the objects. The CreateVehicle() method takes a string parameter that specifies the type of vehicle to create. The method then creates a new instance of the appropriate class based on the specified type.

3. Observer Pattern

The Observer pattern is used to notify objects of changes to the state of other objects. This pattern is commonly used in event-driven systems where multiple objects need to be notified of changes to the state of a single object.

Example:

public interface IObserver {
  void Update(string message);
}

public class ConcreteObserver: IObserver {
  public void Update(string message) {
    Console.WriteLine(message);
  }
}

public interface ISubject {
  void Attach(IObserver observer);
  void Detach(IObserver observer);
  void Notify();
}

public class ConcreteSubject: ISubject {
  private List < IObserver > observers = new List < IObserver > ();
  private string state;

  public string State {
    get {
      return state;
    }
    set {
      state = value;
      Notify();
    }
  }

  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(state);
    }
  }
}

In the above code, the IObserver interface defines the Update() method that is called when the state of the ConcreteSubject object changes. The ConcreteObserver class implements the IObserver interface and defines how it should respond to state changes.

The ISubject interface defines the Attach(), Detach(), and Notify() methods that are used to manage the observers and notify them of changes. The ConcreteSubject class implements the ISubject interface and defines the state variable and the State property that is used to set the state of the object. When the state is changed, the Notify() method is called, which calls the Update() method on each observer.

4. Decorator Pattern

The Decorator pattern is used to add functionality to an object dynamically without changing its structure. This pattern is commonly used when you want to add functionality to an object at runtime without affecting the other objects in the system.

Example:

public abstract class Component {
  public abstract void Operation();
}

public class ConcreteComponent: Component {
  public override void Operation() {
    Console.WriteLine("ConcreteComponent.Operation()");
  }
}

public abstract class Decorator: Component {
  protected Component component;
  public void SetComponent(Component component) {
    this.component = component;
  }

  public override void Operation() {
    if (component != null) {
      component.Operation();
    }
  }
}

public class ConcreteDecoratorA: Decorator {
  public override void Operation() {
    base.Operation();
    Console.WriteLine("ConcreteDecoratorA.Operation()");
  }
}

public class ConcreteDecoratorB: Decorator {
  public override void Operation() {
    base.Operation();
    Console.WriteLine("ConcreteDecoratorB.Operation()");
    AddedBehavior();
  }
  private void AddedBehavior() {
    Console.WriteLine("ConcreteDecoratorB.AddedBehavior()");
  }
}

In the above code, the Component class is an abstract class that defines the basic structure of the object that will be decorated. The ConcreteComponent class is a concrete implementation of the Component class.

The Decorator class is an abstract class that defines the basic structure of the decorator objects. The SetComponent() method is used to set the component that will be decorated. The Operation() method is overridden to call the Operation() method of the component.

The ConcreteDecoratorA and ConcreteDecoratorB classes are concrete implementations of the Decorator class. The ConcreteDecoratorA class adds additional functionality to the component, and the ConcreteDecoratorB class adds even more functionality, including a private AddedBehavior() method that is called from the Operation() method.

5. Strategy Pattern

The Strategy pattern is used to define a family of algorithms and make them interchangeable. This pattern is commonly used when you want to switch between different algorithms at runtime without affecting the other objects in the system.

Example:

public interface IStrategy {
  void Execute();
}

public class ConcreteStrategyA: IStrategy {
  public void Execute() {
    Console.WriteLine("Executing ConcreteStrategyA.");
  }
}

public class ConcreteStrategyB: IStrategy {
  public void Execute() {
    Console.WriteLine("Executing ConcreteStrategyB.");
  }
}

public class Context {
  private IStrategy strategy;
  public Context(IStrategy strategy) {
    this.strategy = strategy;
  }

  public void SetStrategy(IStrategy strategy) {
    this.strategy = strategy;
  }

  public void ExecuteStrategy() {
    strategy.Execute();
  }
}

In the above code, the IStrategy interface defines the Execute() method that is used to execute the algorithm. The ConcreteStrategyA and ConcreteStrategyB classes are concrete implementations of the IStrategy interface.

The Context class is used to manage the strategy objects. The SetStrategy() method is used to set the strategy object, and the ExecuteStrategy() method is used to execute the strategy.

When to Use Design Patterns in C# .Net?

Design patterns are not always necessary, and they should not be used just for the sake of using them. They should only be used when they are needed to solve a particular problem.

Design patterns are useful in the following situations:

  1. When you need to solve a recurring problem that is not related to your business logic.
  2. When you need to make your code more flexible and reusable.
  3. When you need to make your code more maintainable and easier to understand.
  4. When you need to improve the performance of your code.

Conclusion:

Design patterns are an essential part of software development, and they are widely used in the C# .Net programming language. They provide a proven and structured way of solving common software development problems.

In this article, we have discussed some of the most commonly used design patterns in C# .Net, along with sample code and explanations. The patterns discussed in this article are the Singleton pattern, Factory pattern, Observer pattern, Decorator pattern, and Strategy pattern.

Remember that design patterns should only be used when they are needed to solve a particular problem. They should not be used just for the sake of using them.

ABOUT THE AUTHOR

TechieClues
TechieClues

I specialize in creating and sharing insightful content encompassing various programming languages and technologies. My expertise extends to Python, PHP, Java, ... For more detailed information, please check out the user profile

https://www.techieclues.com/profile/techieclues

Comments (0)

There are no comments. Be the first to comment!!!