Real-Time Examples of Strategy Design Pattern in C#

Real-Time Examples of the Strategy Design Pattern in C#

I will discuss the Real-Time Examples of the Strategy Design Pattern in C# in this article. Please read our previous article discussing the basic concepts of Strategy Design Patterns in C# with Examples. At the end of this article, you will understand the following Real-time Examples using Strategy Design Pattern in C#.

  1. Image Processing Filters
  2. Payment Methods
  3. Tax Calculation System
  4. Shipping Cost Calculation
  5. Traffic Navigation System
  6. Document Rendering System
  7. Image Compression
  8. Discount Systems in an E-commerce Application
  9. Text Filtering in a Chat Application
  10. Document Serialization
Real-Time Example of Strategy Design Pattern in C#: Image Processing Filters

Imagine you are building an image processing application that provides various image filters. Each filter is an algorithm that processes an image and gives a result. Some of the filters might be “Black and White”, “Sepia”, “Brightness Adjustment”, etc. Using the Strategy Pattern, each filter can be represented as a strategy. Let us see how we can implement the above example using the Strategy Design Pattern in C#:

using System;
namespace StrategyDesignPattern
{
    // Assume Image is some pre-defined class for this example.
    public class Image
    {
        public string Path { get; set; }
        public string Alt { get; set; }
    }

    //Strategy (Interface)
    public interface IFilterStrategy
    {
        void ApplyFilter(Image image);
    }

    //Concrete Strategies
    public class BlackAndWhiteFilter : IFilterStrategy
    {
        public void ApplyFilter(Image image)
        {
            // Apply black and white filter logic to the image
            Console.WriteLine("Applying Black and White Filter");
        }
    }

    public class SepiaFilter : IFilterStrategy
    {
        public void ApplyFilter(Image image)
        {
            // Apply sepia filter logic to the image
            Console.WriteLine("Applying Sepia Filter");
        }
    }

    public class BrightnessAdjustment : IFilterStrategy
    {
        private int _level;

        public BrightnessAdjustment(int level)
        {
            _level = level;
        }

        public void ApplyFilter(Image image)
        {
            // Adjust the brightness of the image by the given level
            Console.WriteLine($"Adjusting Brightness by level: {_level}");
        }
    }

    //Context
    public class ImageProcessor
    {
        private IFilterStrategy _filter;

        public ImageProcessor(IFilterStrategy filter)
        {
            _filter = filter;
        }

        public void Process(Image image)
        {
            _filter.ApplyFilter(image);
        }
    }
    
    // Testing the Strategy Design Pattern
    // Client Code
    public class Client
    {
        public static void Main()
        {
            Image sampleImage = new Image(); 

            // Apply Black and White filter
            ImageProcessor processor = new ImageProcessor(new BlackAndWhiteFilter());
            processor.Process(sampleImage);

            // Apply Sepia filter
            processor = new ImageProcessor(new SepiaFilter());
            processor.Process(sampleImage);

            // Adjust brightness
            processor = new ImageProcessor(new BrightnessAdjustment(20)); // Increase brightness by level 20
            processor.Process(sampleImage);

            Console.ReadKey();
        }
    }
}

Each filter is a strategy in this example, and the ImageProcessor class is the context. The appropriate processing logic is applied depending on which filter (strategy) you provide to the ImageProcessor. This approach ensures the algorithm for each filter is encapsulated within its respective class, making it easier to add or modify filters without affecting the ImageProcessor or other filters. When you run the above code, you will get the following output.

Real-Time Example of Strategy Design Pattern in C#: Image Processing Filters

Real-Time Example of Strategy Design Pattern in C#: Payment Methods

Imagine you are building an e-commerce system that needs to support multiple payment methods like Credit Card, PayPal, and cryptocurrency. Each payment method has a unique process for payment, and more payment methods may be added in the future. Let us see how we can implement the above example using the Strategy Design Pattern in C#:

using System;
namespace StrategyDesignPattern
{
    //Strategy (Interface)
    public interface IPaymentStrategy
    {
        bool ProcessPayment(double amount);
    }

    //Concrete Strategies
    public class CreditCardPayment : IPaymentStrategy
    {
        private string _cardNumber;
        private string _expiryDate;
        private string _cvc;

        public CreditCardPayment(string cardNumber, string expiryDate, string cvc)
        {
            _cardNumber = cardNumber;
            _expiryDate = expiryDate;
            _cvc = cvc;
        }

        public bool ProcessPayment(double amount)
        {
            // Logic to process credit card payment
            Console.WriteLine($"Processed payment of {amount} using Credit Card");
            return true;
        }
    }

    public class PayPalPayment : IPaymentStrategy
    {
        private string _email;

        public PayPalPayment(string email)
        {
            _email = email;
        }

        public bool ProcessPayment(double amount)
        {
            // Logic to process PayPal payment
            Console.WriteLine($"Processed payment of {amount} using PayPal");
            return true;
        }
    }

    public class CryptoPayment : IPaymentStrategy
    {
        private string _cryptoWalletAddress;

        public CryptoPayment(string walletAddress)
        {
            _cryptoWalletAddress = walletAddress;
        }

        public bool ProcessPayment(double amount)
        {
            // Logic to process cryptocurrency payment
            Console.WriteLine($"Processed payment of {amount} using Cryptocurrency");
            return true;
        }
    }

    //Context
    public class Checkout
    {
        private IPaymentStrategy _paymentMethod;

        public Checkout(IPaymentStrategy paymentMethod)
        {
            _paymentMethod = paymentMethod;
        }

        public bool CompletePurchase(double amount)
        {
            return _paymentMethod.ProcessPayment(amount);
        }
    }
    
    // Testing the Strategy Design Pattern
    // Client Code
    public class Client
    {
        public static void Main()
        {
            double purchaseAmount = 100.00;

            // Choose Credit Card payment
            IPaymentStrategy paymentMethod = new CreditCardPayment("1234567890123456", "12/25", "123");
            Checkout checkout = new Checkout(paymentMethod);
            checkout.CompletePurchase(purchaseAmount);

            // Choose PayPal payment
            paymentMethod = new PayPalPayment("user@example.com");
            checkout = new Checkout(paymentMethod);
            checkout.CompletePurchase(purchaseAmount);

            // Choose Cryptocurrency payment
            paymentMethod = new CryptoPayment("1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa");
            checkout = new Checkout(paymentMethod);
            checkout.CompletePurchase(purchaseAmount);

            Console.ReadKey();
        }
    }
}

In this example, the Checkout class is unaware of the specific payment processing logic. It simply delegates the task to the payment strategy passed to it. Each concrete payment strategy encapsulates the payment processing details for a specific payment method. This design allows for easy extension if more payment methods need to be added in the future without affecting the existing code. When you run the above code, you will get the following output.

Real-Time Example of Strategy Design Pattern in C#: Payment Methods

Real-Time Example of Strategy Design Pattern in C#: Tax Calculation System

Consider a Tax Calculation System for an e-commerce platform that sells products internationally. Countries might have different tax calculation strategies based on their rules and regulations. Let us see how we can implement the above example using the Strategy Design Pattern in C#:

using System;
namespace StrategyDesignPattern
{
    //Strategy (Interface)
    public interface ITaxStrategy
    {
        double CalculateTax(double purchaseAmount);
    }

    //Concrete Strategies
    public class USTaxStrategy : ITaxStrategy
    {
        public double CalculateTax(double purchaseAmount)
        {
            // Example: US has a flat 8% tax rate (this is a simplification)
            return 0.08 * purchaseAmount;
        }
    }

    public class UKTaxStrategy : ITaxStrategy
    {
        public double CalculateTax(double purchaseAmount)
        {
            // Example: UK has a 20% VAT (again, this is a simplification)
            return 0.20 * purchaseAmount;
        }
    }

    public class IndiaTaxStrategy : ITaxStrategy
    {
        public double CalculateTax(double purchaseAmount)
        {
            // Example: India has 18% GST for certain goods (simplification)
            return 0.18 * purchaseAmount;
        }
    }

    //Context
    public class ShoppingCart
    {
        private ITaxStrategy _taxStrategy;
        private double _purchaseAmount;

        public ShoppingCart(ITaxStrategy taxStrategy, double purchaseAmount)
        {
            _taxStrategy = taxStrategy;
            _purchaseAmount = purchaseAmount;
        }

        public double Checkout()
        {
            double tax = _taxStrategy.CalculateTax(_purchaseAmount);
            return _purchaseAmount + tax;
        }
    }
    
    // Testing the Strategy Design Pattern
    // Client Code
    public class Client
    {
        public static void Main()
        {
            double purchaseAmount = 100.00;

            // US Tax Calculation
            ShoppingCart cartUS = new ShoppingCart(new USTaxStrategy(), purchaseAmount);
            Console.WriteLine($"Total Amount (with US tax): {cartUS.Checkout()}");

            // UK Tax Calculation
            ShoppingCart cartUK = new ShoppingCart(new UKTaxStrategy(), purchaseAmount);
            Console.WriteLine($"Total Amount (with UK tax): {cartUK.Checkout()}");

            // India Tax Calculation
            ShoppingCart cartIndia = new ShoppingCart(new IndiaTaxStrategy(), purchaseAmount);
            Console.WriteLine($"Total Amount (with India tax): {cartIndia.Checkout()}");

            Console.ReadKey();
        }
    }
}

In this example, the tax calculation logic for each country is encapsulated within the respective strategy. The ShoppingCart class, which is the context, doesn’t have to be aware of the specific tax rules of each country. Instead, it delegates the tax calculation responsibility to the strategy passed to it.

This design allows easy extension if tax rules change or support for more countries needs to be added. The context and existing strategies remain unaffected, thus ensuring the system remains flexible and maintainable. When you run the above code, you will get the following output.

Real-Time Example of Strategy Design Pattern in C#: Tax Calculation System

Real-Time Example of Strategy Design Pattern in C#: Shipping Cost Calculation

Let’s consider Shipping Cost Calculation for an e-commerce system. Various shipping methods (like Express, Standard, or Economy) might be available, and each will have its own strategy for calculating the cost based on factors like weight, destination, and delivery speed. Let us see how we can implement the above example using the Strategy Design Pattern in C#:

using System;
namespace StrategyDesignPattern
{
    //Strategy (Interface)
    public interface IShippingStrategy
    {
        double CalculateCost(double weight, string destination);
    }

    //Concrete Strategies
    public class ExpressShipping : IShippingStrategy
    {
        public double CalculateCost(double weight, string destination)
        {
            // Express shipping has a base cost and increases with weight
            return 10 + (2 * weight);
        }
    }

    public class StandardShipping : IShippingStrategy
    {
        public double CalculateCost(double weight, string destination)
        {
            // Standard shipping has a fixed cost regardless of weight
            return 5;
        }
    }

    public class EconomyShipping : IShippingStrategy
    {
        public double CalculateCost(double weight, string destination)
        {
            // Economy shipping costs decrease as weight increases
            return 15 - (0.1 * weight);
        }
    }

    //Context
    public class Order
    {
        private IShippingStrategy _shippingStrategy;
        private double _weight;
        private string _destination;

        public Order(IShippingStrategy shippingStrategy, double weight, string destination)
        {
            _shippingStrategy = shippingStrategy;
            _weight = weight;
            _destination = destination;
        }

        public double GetTotalShippingCost()
        {
            return _shippingStrategy.CalculateCost(_weight, _destination);
        }
    }
    
    // Testing the Strategy Design Pattern
    // Client Code
    public class Client
    {
        public static void Main()
        {
            double weight = 10.00; // Assume 10kg package weight
            string destination = "New York"; // Destination city (could be used in more complex strategies)

            // Express Shipping Calculation
            Order expressOrder = new Order(new ExpressShipping(), weight, destination);
            Console.WriteLine($"Total Shipping Cost (Express): {expressOrder.GetTotalShippingCost()}");

            // Standard Shipping Calculation
            Order standardOrder = new Order(new StandardShipping(), weight, destination);
            Console.WriteLine($"Total Shipping Cost (Standard): {standardOrder.GetTotalShippingCost()}");

            // Economy Shipping Calculation
            Order economyOrder = new Order(new EconomyShipping(), weight, destination);
            Console.WriteLine($"Total Shipping Cost (Economy): {economyOrder.GetTotalShippingCost()}");

            Console.ReadKey();
        }
    }
}

In this example, the Order class calculates the total shipping cost using a shipping strategy. Different strategies can be plugged in to calculate the cost differently based on shipping methods. If a new shipping method or new rules for existing methods are introduced in the future, you can add a new strategy or update the existing one without affecting other parts of the code. When you run the above code, you will get the following output.

Real-Time Example of Strategy Design Pattern in C#: Shipping Cost Calculation

Real-Time Example of Strategy Design Pattern in C#: Traffic Navigation System

Let’s consider the scenario of a Traffic Navigation System. The system needs to find the best route based on different strategies: the fastest, shortest, or most scenic routes. Let us see how we can implement the above example using the Strategy Design Pattern in C#:

using System;
namespace StrategyDesignPattern
{
    //Strategy (Interface)
    public interface IRouteStrategy
    {
        string FindRoute(string start, string end);
    }

    //Concrete Strategies
    public class FastestRoute : IRouteStrategy
    {
        public string FindRoute(string start, string end)
        {
            // Logic to find the fastest route based on current traffic, highways, etc.
            return $"Fastest route from {start} to {end} via Highway A1.";
        }
    }

    public class ShortestRoute : IRouteStrategy
    {
        public string FindRoute(string start, string end)
        {
            // Logic to find the shortest distance route, might not always be the fastest.
            return $"Shortest route from {start} to {end} via Local Road L45.";
        }
    }

    public class ScenicRoute : IRouteStrategy
    {
        public string FindRoute(string start, string end)
        {
            // Logic to find the most scenic route, might be longer in terms of distance and time.
            return $"Scenic route from {start} to {end} via Coastline Road C23.";
        }
    }

    //Context
    public class NavigationSystem
    {
        private IRouteStrategy _routeStrategy;

        public NavigationSystem(IRouteStrategy routeStrategy)
        {
            _routeStrategy = routeStrategy;
        }

        public string Navigate(string start, string end)
        {
            return _routeStrategy.FindRoute(start, end);
        }
    }
    
    // Testing the Strategy Design Pattern
    // Client Code
    public class Client
    {
        public static void Main()
        {
            string startingPoint = "Point A";
            string destination = "Point B";

            // Navigate using the Fastest Route
            NavigationSystem navSystem = new NavigationSystem(new FastestRoute());
            Console.WriteLine(navSystem.Navigate(startingPoint, destination));

            // Navigate using the Shortest Route
            navSystem = new NavigationSystem(new ShortestRoute());
            Console.WriteLine(navSystem.Navigate(startingPoint, destination));

            // Navigate using the Scenic Route
            navSystem = new NavigationSystem(new ScenicRoute());
            Console.WriteLine(navSystem.Navigate(startingPoint, destination));

            Console.ReadKey();
        }
    }
}

In this example, the NavigationSystem delegates the responsibility of finding a route to the strategy provided. If, in the future, there’s a need to introduce a new route-finding strategy (like a bike-friendly route or a walking route), it can be easily added without modifying the existing strategies or the NavigationSystem. This design ensures flexibility and adheres to the Open/Closed Principle (software entities should be open for extension but closed for modification). When you run the above code, you will get the following output.

Real-Time Example of Strategy Design Pattern in C#: Traffic Navigation System

Real-Time Example of Strategy Design Pattern in C#: Document Rendering System

Let’s consider another real-world example: a Document Rendering System. Imagine a software that can export documents in formats like PDF, Markdown, or HTML. Let us see how we can implement the above example using the Strategy Design Pattern in C#:

using System;
namespace StrategyDesignPattern
{
    //Strategy (Interface)
    public interface IDocumentRenderStrategy
    {
        void RenderContent(string content);
    }

    //Concrete Strategies
    public class PDFRenderStrategy : IDocumentRenderStrategy
    {
        public void RenderContent(string content)
        {
            // Logic to render content as PDF (this is a simplification)
            Console.WriteLine($"Rendering content as PDF: {content}");
        }
    }

    public class MarkdownRenderStrategy : IDocumentRenderStrategy
    {
        public void RenderContent(string content)
        {
            // Logic to render content as Markdown
            Console.WriteLine($"Rendering content as Markdown: {content}");
        }
    }

    public class HTMLRenderStrategy : IDocumentRenderStrategy
    {
        public void RenderContent(string content)
        {
            // Logic to render content as HTML
            Console.WriteLine($"Rendering content as HTML: <div>{content}</div>");
        }
    }

    //Context
    public class DocumentRenderer
    {
        private IDocumentRenderStrategy _renderStrategy;

        public DocumentRenderer(IDocumentRenderStrategy renderStrategy)
        {
            _renderStrategy = renderStrategy;
        }

        public void Render(string content)
        {
            _renderStrategy.RenderContent(content);
        }
    }
    
    // Testing the Strategy Design Pattern
    // Client Code
    public class Client
    {
        public static void Main()
        {
            string documentContent = "This is a sample content for our document.";

            // Render as PDF
            DocumentRenderer renderer = new DocumentRenderer(new PDFRenderStrategy());
            renderer.Render(documentContent);

            // Render as Markdown
            renderer = new DocumentRenderer(new MarkdownRenderStrategy());
            renderer.Render(documentContent);

            // Render as HTML
            renderer = new DocumentRenderer(new HTMLRenderStrategy());
            renderer.Render(documentContent);

            Console.ReadKey();
        }
    }
}

In this system, DocumentRenderer is the context that delegates the responsibility of rendering a document to the strategy injected into it. By adopting the Strategy Design Pattern, the system can easily be extended to support new document rendering formats in the future without having to modify the existing classes. This maintains flexibility and adherence to the Open/Closed Principle of SOLID. When you run the above code, you will get the following output.

Real-Time Example of Strategy Design Pattern in C#: Document Rendering System

Real-Time Example of Strategy Design Pattern in C#: Image Compression

Let’s understand another real-time example: Image Compression. Assume you have an image editor application and want to offer the users multiple compression algorithms. Let us see how we can implement the above example using the Strategy Design Pattern in C#:

using System;
namespace StrategyDesignPattern
{
    //Strategy (Interface)
    public interface ICompressionStrategy
    {
        void Compress(string fileName);
    }

    //Concrete Strategies
    public class JPEGCompression : ICompressionStrategy
    {
        public void Compress(string fileName)
        {
            // Logic to compress using JPEG (simplified for illustration)
            Console.WriteLine($"Compressing {fileName} using JPEG compression.");
        }
    }

    public class PNGCompression : ICompressionStrategy
    {
        public void Compress(string fileName)
        {
            // Logic to compress using PNG
            Console.WriteLine($"Compressing {fileName} using PNG compression.");
        }
    }

    public class GIFCompression : ICompressionStrategy
    {
        public void Compress(string fileName)
        {
            // Logic to compress using GIF
            Console.WriteLine($"Compressing {fileName} using GIF compression.");
        }
    }

    //Context
    public class ImageEditor
    {
        private ICompressionStrategy _compressionStrategy;

        public ImageEditor(ICompressionStrategy compressionStrategy)
        {
            _compressionStrategy = compressionStrategy;
        }

        public void SaveImage(string fileName)
        {
            _compressionStrategy.Compress(fileName);
        }
    }
    
    // Testing the Strategy Design Pattern
    // Client Code
    public class Client
    {
        public static void Main()
        {
            string imageName = "sampleImage";

            // Save image using JPEG Compression
            ImageEditor editor = new ImageEditor(new JPEGCompression());
            editor.SaveImage(imageName);

            // Save image using PNG Compression
            editor = new ImageEditor(new PNGCompression());
            editor.SaveImage(imageName);

            // Save image using GIF Compression
            editor = new ImageEditor(new GIFCompression());
            editor.SaveImage(imageName);

            Console.ReadKey();
        }
    }
}

The ImageEditor class lets users save images, but the exact compression strategy is abstracted. Users can choose or switch between compression methods easily. If, in the future, a new compression method is developed (like WebP or another advanced format), a new strategy can be created and incorporated without modifying the existing strategies or the main ImageEditor class. This design promotes scalability and flexibility. When you run the above code, you will get the following output.

Real-Time Example of Strategy Design Pattern in C#: Image Compression

Real-Time Example of Strategy Design Pattern in C#: Discount Systems

Let’s understand the example of Discount Systems for an e-commerce platform. This platform allows different types of discounts: seasonal, loyalty, or special event discounts. Let us see how we can implement the above example using the Strategy Design Pattern in C#:

using System;
namespace StrategyDesignPattern
{
    //Strategy (Interface)
    public interface IDiscountStrategy
    {
        double ApplyDiscount(double originalPrice);
    }

    //Concrete Strategies
    public class SeasonalDiscount : IDiscountStrategy
    {
        public double ApplyDiscount(double originalPrice)
        {
            // For this example, let's assume a 10% discount for seasonal promotions.
            return originalPrice * 0.9;
        }
    }

    public class LoyaltyDiscount : IDiscountStrategy
    {
        public double ApplyDiscount(double originalPrice)
        {
            // For loyal customers, the platform offers a 15% discount.
            return originalPrice * 0.85;
        }
    }

    public class EventDiscount : IDiscountStrategy
    {
        public double ApplyDiscount(double originalPrice)
        {
            // For special events, there's a 20% discount.
            return originalPrice * 0.8;
        }
    }

    //Context
    public class Product
    {
        public string Name { get; set; }
        public double Price { get; set; }
        private IDiscountStrategy _discountStrategy;

        public Product(string name, double price, IDiscountStrategy discountStrategy)
        {
            Name = name;
            Price = price;
            _discountStrategy = discountStrategy;
        }

        public double GetDiscountedPrice()
        {
            return _discountStrategy.ApplyDiscount(Price);
        }
    }
    
    // Testing the Strategy Design Pattern
    // Client Code
    public class Client
    {
        public static void Main()
        {
            // Product without any discount
            Product normalProduct = new Product("Shirt", 100, new SeasonalDiscount());
            Console.WriteLine($"Price of {normalProduct.Name} after Seasonal Discount: ${normalProduct.GetDiscountedPrice()}");

            // Product with loyalty discount
            Product loyalProduct = new Product("Pants", 150, new LoyaltyDiscount());
            Console.WriteLine($"Price of {loyalProduct.Name} after Loyalty Discount: ${loyalProduct.GetDiscountedPrice()}");

            // Product with event discount
            Product eventProduct = new Product("Shoes", 200, new EventDiscount());
            Console.WriteLine($"Price of {eventProduct.Name} after Event Discount: ${eventProduct.GetDiscountedPrice()}");

            Console.ReadKey();
        }
    }
}

In this e-commerce platform example, the Product class allows for flexible discounting mechanisms. By utilizing the Strategy Design Pattern, the platform can quickly introduce new types of discounts in the future, such as bulk purchase discounts or referral discounts, without changing the core Product class or existing discount strategies. When you run the above code, you will get the following output.

Real-Time Example of Strategy Design Pattern in C#: Discount Systems

Real-Time Example of Strategy Design Pattern in C#: Text Filtering in a Chat Application

Let’s consider a chat application where users can opt for different types of text filtering: profanity filtering, link removal, or emoji removal. Let us see how we can implement the above example using the Strategy Design Pattern in C#:

using System;
using System.Text.RegularExpressions;

namespace StrategyDesignPattern
{
    //Strategy (Interface)
    public interface ITextFilterStrategy
    {
        string Filter(string text);
    }

    //Concrete Strategies
    public class ProfanityFilter : ITextFilterStrategy
    {
        public string Filter(string text)
        {
            // For simplicity, just replace a few words
            return text.Replace("badword", "***").Replace("offensive", "***");
        }
    }

    public class LinkRemovalFilter : ITextFilterStrategy
    {
        public string Filter(string text)
        {
            // Logic to remove hyperlinks
            var regex = new Regex(@"http(s)?://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?");
            return regex.Replace(text, "[link removed]");
        }
    }

    public class EmojiRemovalFilter : ITextFilterStrategy
    {
        public string Filter(string text)
        {
            // Logic to remove common emoji patterns (very simplified)
            return text.Replace(":)", "").Replace(":-)", "").Replace(":(", "");
        }
    }

    //Context
    public class ChatProcessor
    {
        private ITextFilterStrategy _filterStrategy;

        public ChatProcessor(ITextFilterStrategy filterStrategy)
        {
            _filterStrategy = filterStrategy;
        }

        public void SetFilterStrategy(ITextFilterStrategy filterStrategy)
        {
            _filterStrategy = filterStrategy;
        }

        public string ProcessMessage(string message)
        {
            return _filterStrategy.Filter(message);
        }
    }

    // Testing the Strategy Design Pattern
    // Client Code
    public class Client
    {
        public static void Main()
        {
            string message = "Hey! Here is a badword in this sentence. Visit http://example.com and enjoy :)";

            // Process with profanity filter
            ChatProcessor processor = new ChatProcessor(new ProfanityFilter());
            Console.WriteLine(processor.ProcessMessage(message));

            // Switch to link removal filter
            processor.SetFilterStrategy(new LinkRemovalFilter());
            Console.WriteLine(processor.ProcessMessage(message));

            // Switch to emoji removal filter
            processor.SetFilterStrategy(new EmojiRemovalFilter());
            Console.WriteLine(processor.ProcessMessage(message));

            Console.ReadKey();
        }
    }
}

The ChatProcessor class can use different text filtering mechanisms based on user settings or platform requirements in this chat application context. By employing the Strategy Design Pattern, adding more filters (like markdown conversion, code syntax highlighting, etc.) in the future is easy without modifying the core ChatProcessor class or the existing filtering strategies. When you run the above code, you will get the following output.

Real-Time Example of Strategy Design Pattern in C#: Text Filtering in a Chat Application

Real-Time Example of Strategy Design Pattern in C#: Document Serialization

Let’s explore the context of Document Serialization in an application that supports different serialization formats such as XML, JSON, and Binary. Let us see how we can implement the above example using the Strategy Design Pattern in C#:

using System;
namespace StrategyDesignPattern
{
    //Strategy (Interface)
    public interface IDocumentSerializationStrategy
    {
        void Serialize(string document, string outputPath);
    }

    //Concrete Strategies
    public class XmlSerialization : IDocumentSerializationStrategy
    {
        public void Serialize(string document, string outputPath)
        {
            // Logic for XML serialization (simplified for example)
            Console.WriteLine($"Serializing document {document} into XML format at {outputPath}.");
        }
    }

    public class JsonSerialization : IDocumentSerializationStrategy
    {
        public void Serialize(string document, string outputPath)
        {
            // Logic for JSON serialization
            Console.WriteLine($"Serializing document {document} into JSON format at {outputPath}.");
        }
    }

    public class BinarySerialization : IDocumentSerializationStrategy
    {
        public void Serialize(string document, string outputPath)
        {
            // Logic for Binary serialization
            Console.WriteLine($"Serializing document {document} into Binary format at {outputPath}.");
        }
    }

    //Context
    public class DocumentProcessor
    {
        private IDocumentSerializationStrategy _serializationStrategy;

        public DocumentProcessor(IDocumentSerializationStrategy serializationStrategy)
        {
            _serializationStrategy = serializationStrategy;
        }

        public void SetSerializationStrategy(IDocumentSerializationStrategy serializationStrategy)
        {
            _serializationStrategy = serializationStrategy;
        }

        public void ProcessDocument(string document, string outputPath)
        {
            _serializationStrategy.Serialize(document, outputPath);
        }
    }
    
    // Testing the Strategy Design Pattern
    // Client Code
    public class Client
    {
        public static void Main()
        {
            string documentContent = "This is a sample document content.";

            // Serialize using XML strategy
            DocumentProcessor processor = new DocumentProcessor(new XmlSerialization());
            processor.ProcessDocument(documentContent, "document.xml");

            // Switch to JSON serialization
            processor.SetSerializationStrategy(new JsonSerialization());
            processor.ProcessDocument(documentContent, "document.json");

            // Switch to Binary serialization
            processor.SetSerializationStrategy(new BinarySerialization());
            processor.ProcessDocument(documentContent, "document.bin");

            Console.ReadKey();
        }
    }
}

The DocumentProcessor class can adopt different serialization mechanisms based on user preference or application requirements in this document processing scenario. Thanks to the Strategy Design Pattern, the application can easily support new serialization formats in the future, like YAML or Protocol Buffers, without modifying the core DocumentProcessor class or the existing serialization strategies. When you run the above code, you will get the following output.

Real-Time Example of Strategy Design Pattern in C#: Document Serialization

When to use the Strategy Design Pattern in Real-Time Applications?

The Strategy Design Pattern is useful in various real-time scenarios. Consider using the Strategy Pattern when:

  • Multiple Variants of an Algorithm are Available: If there are different ways to perform an operation and you might need to switch between them dynamically, the Strategy Pattern is a good fit. For instance, if you have various sorting algorithms, you can encapsulate each one as a strategy.
  • Algorithm Specific Data: Sometimes, certain algorithms might need data that others don’t. Encapsulating this data inside the concrete strategy can help provide a cleaner interface.
  • Decoupling Algorithm from Client: If you want to decouple the specifics of the algorithm from the class that uses it, thereby making the client class unaware of the algorithm’s specifics, the Strategy Pattern can be employed.
  • Dynamic Strategy Selection: If the application needs to choose among several algorithms dynamically, Strategy Pattern provides a cleaner way to switch at runtime.
  • Extension Over Modification: If you anticipate introducing new algorithms or behaviors without modifying existing code (in adherence with the Open/Closed Principle), the Strategy Pattern is beneficial.
  • Unit Testing: The Strategy Pattern can make unit testing easier by isolating the algorithm from the client class. You can then write unit tests for each strategy independently.

Real-time applications/examples:

  • Compression Algorithms: Suppose you are building a file archiver like ZIP. Users might want to choose between multiple compression algorithms, like LZ77, Huffman, or RLE. The Strategy Pattern can help encapsulate each compression algorithm and allow easy switching.
  • Payment Strategies: In e-commerce systems, there are multiple payment methods – Credit card, PayPal, direct bank transfer, etc. Each payment method can be a separate strategy.
  • Rendering Algorithms: Different rendering algorithms might be suitable for different situations in graphics. For example, a graphics program might offer different algorithms for anti-aliasing.
  • Travel Route Planning: Consider a navigation application where users can choose the route they want: fastest route, shortest route, or scenic route. Each routing algorithm can be implemented as a strategy.
  • Validation Strategies: In form validation, based on user selection, you might want to apply different validation strategies (e.g., stringent validation for high-security forms and basic validation for low-priority forms).
  • Discounting Algorithms: As described in the previous example, an e-commerce platform might offer different discounting techniques – festive season discount, buy one get one free, etc.
  • Database Connection: In applications where you might need to connect to different databases (SQL, NoSQL, Graph, etc.), each connection strategy can be encapsulated using the Strategy Pattern.

So, in summary, use the Strategy Design Pattern when you have multiple ways to achieve something and want to switch between them seamlessly without altering the code that uses these algorithms.

Advantages and Disadvantages Strategy Design Pattern in C#

The Strategy Design Pattern, like any other pattern, has its own set of advantages and disadvantages:

Advantages of Strategy Design Pattern in C#:
  1. Flexibility: Strategy Pattern promotes the Open/Closed Principle, which means you can introduce new strategies without modifying the context class or any existing strategies.
  2. Decoupling: It decouples the algorithm’s implementation from the client class. As a result, the system becomes more modular and easier to maintain.
  3. Switching Strategies at Runtime: The pattern allows you to switch strategies on the fly during runtime, providing dynamic behavior to the client.
  4. Testing: By decoupling the client from the strategies, you can independently test each strategy without needing the client. This makes unit testing more straightforward.
  5. Reusable: The strategies can be reused across different contexts. For instance, a compression algorithm strategy used in one application can be reused in another without modification.
  6. Avoids Conditional Statements: Without the Strategy Pattern, you might have to use several conditional statements to decide which algorithm to run. With this pattern, you avoid that, making the code more readable and maintainable.
Disadvantages of Strategy Design Pattern in C#:
  1. Overhead: For simple scenarios, introducing the Strategy Pattern can lead to unnecessary classes, adding complexity and overhead.
  2. Learning Curve: If there are many strategies, and a developer is new to the system, it might be slightly more challenging to understand which strategy to use and when.
  3. Client-Strategy Communication: Sometimes, the strategy might need to access some data from the client. This can be tricky as the pattern generally promotes decoupling. You might have to provide more context to the strategy, leading to tighter coupling than desired.
  4. Initialization Overhead: Each strategy, when used, would often be instantiated as a new object. This can create a performance overhead, especially if strategies are frequently changed at runtime.
  5. Potential Duplication: If not designed carefully, there might be some overlapping code among strategies. This could lead to duplication, which can be a maintenance concern.

In conclusion, while the Strategy Pattern offers many advantages related to flexibility, maintainability, and testability, ensuring it’s the right fit for the given problem is essential. Using it unnecessarily can lead to added complexity. As with any design pattern, properly understanding the problem domain and the pattern itself is crucial before deciding on its application.

In the next article, I will discuss the Interpreter Design Pattern in C# with Examples. Here, in this article, I try to explain Real-Time Examples of Strategy Design Patterns in C#. I hope you enjoy this Real-Time Example of the Strategy Design Pattern using the C# article.

1 thought on “Real-Time Examples of Strategy Design Pattern in C#”

Leave a Reply

Your email address will not be published. Required fields are marked *