In the realm of software design, achieving flexibility and maintainability is a constant pursuit. One design pattern that elegantly addresses these concerns is the Strategy Pattern. This pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. In this blog post, we’ll delve into the Strategy Pattern using a practical example in C#.

Understanding the Example

Let’s explore a scenario involving a Shop class. This shop allows customers to pay using different payment gateways, such as Google Pay and Apple Pay. The Strategy Pattern comes into play by enabling the Shop to dynamically change its payment strategy without altering its core structure.

namespace Strategy
{
    public class Shop
    {
        private ICheckOut _checkOut;

        public Shop(ICheckOut checkOut)
        {
            this._checkOut = checkOut;
        }

        public void ChangeCheckOut(ICheckOut check)
        {
            _checkOut = check;
        }

        public string Pay(double price)
        {
            return _checkOut.PayWith(price);
        }
    }
}

namespace Strategy
{
    public interface ICheckOut
    {
        string PayWith(double price);
    }
}

namespace Strategy
{
    public class CheckOutWithGoogle : ICheckOut
    {
        public string PayWith(double price)
        {
            return "pay with Google OK";
        }
    }
}

namespace Strategy
{
    public class CheckOutWithApple : ICheckOut
    {
        public string PayWith(double price)
        {
            return "pay with Apple OK";
        }
    }
}

Breaking Down the Components

  1. Shop Class:
    • The Shop class contains a private _checkOut field of type ICheckOut, representing the payment strategy.
    • It has a constructor to set the initial payment strategy and methods to change the payment strategy (ChangeCheckOut) and initiate payment (Pay).
  2. ICheckOut Interface:
    • The ICheckOut interface declares a method PayWith that takes the payment amount and returns a string indicating the success of the payment.
  3. CheckOutWithGoogle and CheckOutWithApple Classes:
    • Classes implementing the ICheckOut interface with specific payment logic for Google Pay and Apple Pay.

Using the Strategy Pattern

Now, let’s see how the Strategy Pattern empowers the Shop class:

// Creating a Shop with Google Pay
var shop = new Shop(new CheckOutWithGoogle());
var result = shop.Pay(100.0); // "pay with Google OK"

// Changing the payment strategy to Apple Pay
shop.ChangeCheckOut(new CheckOutWithApple());
result = shop.Pay(150.0); // "pay with Apple OK"

Advantages of the Strategy Pattern

  1. Flexibility:
    • The Shop class can dynamically switch between different payment strategies, allowing for flexibility in adapting to changing requirements.
  2. Maintainability:
    • Adding new payment strategies (e.g., for additional payment gateways) is seamless without modifying existing code.
  3. Testability:
    • Each payment strategy can be tested independently, ensuring robustness.

Conclusion

The Strategy Pattern proves to be a valuable asset in designing software systems that require flexibility in algorithm selection. By encapsulating algorithms into separate classes, the Strategy Pattern promotes clean, maintainable, and extensible code. In the context of the Shop class, the ability to dynamically switch payment strategies demonstrates the pattern’s prowess in simplifying complex scenarios. Integrate the Strategy Pattern into your design toolkit to master flexibility and elevate your software design practices.

Happy coding!

By Daniel

Leave a Reply

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