Advanced .NET Core Design Patterns with Code Examples

Introduction

In the world of software development, design patterns are proven solutions to common architectural challenges. For .NET Core developers, mastering these patterns is essential to building robust, scalable, and maintainable applications. This article explores some of the most advanced design patterns in .NET Core, complete with practical code examples to help you implement these patterns in your projects.

1. The Command Pattern

The Command Pattern is a behavioral design pattern that turns a request into a stand-alone object that contains all the information about the request. This pattern is particularly useful in scenarios where you need to decouple objects that issue requests from those that execute them, such as in task scheduling or undo mechanisms.

Code Example

public interface ICommand
{
    void Execute();
}

public class Light
{
    public void TurnOn() => Console.WriteLine("The light is on.");
    public void TurnOff() => Console.WriteLine("The light is off.");
}

public class TurnOnCommand : ICommand
{
    private readonly Light _light;

    public TurnOnCommand(Light light) => _light = light;

    public void Execute() => _light.TurnOn();
}

public class TurnOffCommand : ICommand
{
    private readonly Light _light;

    public TurnOffCommand(Light light) => _light = off

    public void Execute() => _light.TurnOff();
}

// Usage
var light = new Light();
ICommand turnOn = new TurnOnCommand(light);
ICommand turnOff = new TurnOffCommand(light);

turnOn.Execute();  // Output: The light is on.
turnOff.Execute(); // Output: The light is off.

2. The Strategy Pattern

The Strategy Pattern allows you to define a family of algorithms, encapsulate each one, and make them interchangeable. This pattern is useful in scenarios where you need to switch between different algorithms or operations dynamically.

Code Example

public interface ICompressionStrategy
{
    void Compress(string filePath);
}

public class ZipCompressionStrategy : ICompressionStrategy
{
    public void Compress(string filePath)
    {
        Console.WriteLine($"{filePath} is compressed using ZIP compression.");
    }
}

public class RarCompressionStrategy : ICompressionStrategy
{
    public void Compress(string filePath)
    {
        Console.WriteLine($"{filePath} is compressed using RAR compression.");
    }
}

public class CompressionContext
{
    private ICompressionStrategy _compressionStrategy;

    public void SetStrategy(ICompressionStrategy compressionStrategy)
    {
        _compressionStrategy = compressionStrategy;
    }

    public void CreateArchive(string filePath)
    {
        _compressionStrategy.Compress(filePath);
    }
}

// Usage
var context = new CompressionContext();
context.SetStrategy(new ZipCompressionStrategy());
context.CreateArchive("myfile.txt"); // Output: myfile.txt is compressed using ZIP compression.
context.SetStrategy(new RarCompressionStrategy());
context.CreateArchive("myfile.txt"); // Output: myfile.txt is compressed using RAR compression.

3. The Decorator Pattern

The Decorator Pattern allows you to add new functionality to an object without altering its structure. This is particularly useful when you want to add responsibilities to individual objects dynamically and transparently, keeping the code modular and reusable.

Code Example

public interface ICoffee
{
    string GetDescription();
    double GetCost();
}

public class SimpleCoffee : ICoffee
{
    public string GetDescription() => "Simple coffee";
    public double GetCost() => 2.0;
}

public class MilkDecorator : ICoffee
{
    private readonly ICoffee _coffee;

    public MilkDecorator(ICoffee coffee) => _coffee = coffee;

    public string GetDescription() => _coffee.GetDescription() + ", milk";
    public double GetCost() => _coffee.GetCost() + 0.5;
}

public class SugarDecorator : ICoffee
{
    private readonly ICoffee _coffee;

    public SugarDecorator(ICoffee coffee) => _coffee = coffee;

    public string GetDescription() => _coffee.GetDescription() + ", sugar";
    public double GetCost() => _coffee.GetCost() + 0.2;
}

// Usage
ICoffee coffee = new SimpleCoffee();
Console.WriteLine($"{coffee.GetDescription()} costs {coffee.GetCost()}");

coffee = new MilkDecorator(coffee);
Console.WriteLine($"{coffee.GetDescription()} costs {coffee.GetCost()}");

coffee = new SugarDecorator(coffee);
Console.WriteLine($"{coffee.GetDescription()} costs {coffee.GetCost()}");

Conclusion

Design patterns are critical tools for .NET Core developers aiming to create efficient, scalable, and maintainable applications. The Command, Strategy, and Decorator patterns are just a few examples of how these patterns can be implemented in .NET Core. By mastering these patterns, you can enhance your ability to solve complex software design problems and improve the quality of your codebase.