Hi devs,
The Strategy Pattern is a behavioral design pattern that allows you to define a family of algorithms, encapsulate each one, and make them interchangeable. It helps in situations where multiple strategies (algorithms) can be used depending on the context, making the code more flexible and easier to maintain.
In this article, we’ll explain the Strategy Pattern by calculating VAT (Value Added Tax) for three different countries: Portugal (PT), France (FR), and Spain (SP). The idea is to show how you can easily switch between the different VAT calculation strategies without altering the core application logic.
Why Use the Strategy Pattern?
In real-world applications, there are often multiple ways to perform a certain task. For example, VAT rates differ across countries. Without the Strategy Pattern, you might end up using complex if-else or switch-case statements to determine the correct VAT calculation, which can make your code harder to read and maintain. The Strategy Pattern allows you to offload this complexity into separate, easily interchangeable classes.
The Problem: Calculating VAT for Different Countries
Let’s assume that we are building a retail application that needs to calculate the VAT for different countries. Each country has its own VAT rate and rules:
- Portugal (PT): 23% VAT
- France (FR): 20% VAT
- Spain (SP): 21% VAT
Instead of hardcoding these rules, we’ll use the Strategy Pattern to encapsulate each VAT calculation in its own class.
Step 1: Define the Strategy Interface
We start by defining an interface IVatStrategy
, which will declare the method CalculateVat()
.
public interface IVatStrategy
{
decimal CalculateVat(decimal amount);
}
Step 2: Implement the Strategy Classes
Next, we implement the strategy for each country.
Portugal VAT Strategy:
public class PortugalVatStrategy : IVatStrategy
{
public decimal CalculateVat(decimal amount)
{
return amount * 0.23m;
}
}
France VAT Strategy:
public class FranceVatStrategy : IVatStrategy
{
public decimal CalculateVat(decimal amount)
{
return amount * 0.20m;
}
}
Spain VAT Strategy:
public class SpainVatStrategy : IVatStrategy
{
public decimal CalculateVat(decimal amount)
{
return amount * 0.21m;
}
}
Each class adheres to the IVatStrategy
interface, providing its own implementation of the CalculateVat
method.
Step 3: The Context Class
The context class is responsible for using the strategy. It doesn’t care about the specific implementation of the VAT calculation; it simply relies on the strategy interface.
public class VatCalculator
{
private IVatStrategy _vatStrategy;
public VatCalculator(IVatStrategy vatStrategy)
{
_vatStrategy = vatStrategy;
}
public decimal CalculateVat(decimal amount)
{
return _vatStrategy.CalculateVat(amount);
}
}
This class takes a strategy as a parameter and delegates the VAT calculation to the strategy.
Step 4: Client Code
Now, we can use the VatCalculator
class in our client code to calculate VAT for any country.
class Program
{
static void Main(string[] args)
{
decimal amount = 100.00m;
// Portugal VAT
VatCalculator vatCalculator = new VatCalculator(new PortugalVatStrategy());
Console.WriteLine("VAT in Portugal: " + vatCalculator.CalculateVat(amount));
// France VAT
vatCalculator = new VatCalculator(new FranceVatStrategy());
Console.WriteLine("VAT in France: " + vatCalculator.CalculateVat(amount));
// Spain VAT
vatCalculator = new VatCalculator(new SpainVatStrategy());
Console.WriteLine("VAT in Spain: " + vatCalculator.CalculateVat(amount));
}
}
Output:
VAT in Portugal: 23.00
VAT in France: 20.00
VAT in Spain: 21.00
Why the Strategy Pattern Is Beneficial
-
Extensibility: Adding a new country with a different VAT rate is as simple as creating a new class that implements
IVatStrategy
. The client code remains unaffected. - Readability: The code is cleaner because the VAT logic is encapsulated in separate classes rather than cluttering the main code with conditionals.
- Maintainability: If a VAT rate changes, you only need to modify the relevant strategy class.
Conclusion
The Strategy Pattern allows for more flexible and maintainable code by decoupling algorithms from the context where they are used. In our VAT example, each country’s VAT calculation is encapsulated in its own strategy class, making it easy to add or modify countries without affecting the rest of the application. This pattern is particularly useful when you have multiple algorithms for a task and need to switch between them seamlessly.
Top comments (0)