Strategy Pattern
Part of the Object Oriented Programming section of Coddy's C# journey — lesson 55 of 70.
The Strategy pattern is a behavioral pattern that lets you define a family of algorithms, encapsulate each one in its own class, and make them interchangeable at runtime. Instead of hardcoding behavior, you inject the desired strategy into an object.
The pattern consists of three parts: a strategy interface that defines the algorithm contract, concrete strategy classes that implement different behaviors, and a context class that uses a strategy:
public interface IPaymentStrategy
{
void Pay(decimal amount);
}
public class CreditCardPayment : IPaymentStrategy
{
public void Pay(decimal amount) =>
Console.WriteLine($"Paid {amount} via Credit Card");
}
public class PayPalPayment : IPaymentStrategy
{
public void Pay(decimal amount) =>
Console.WriteLine($"Paid {amount} via PayPal");
}
public class ShoppingCart
{
private IPaymentStrategy _paymentStrategy;
public void SetPaymentMethod(IPaymentStrategy strategy)
{
_paymentStrategy = strategy;
}
public void Checkout(decimal total)
{
_paymentStrategy.Pay(total);
}
}The context (ShoppingCart) doesn't know which payment method it's using - it just calls Pay() on whatever strategy was provided:
var cart = new ShoppingCart();
cart.SetPaymentMethod(new CreditCardPayment());
cart.Checkout(99.99m); // Paid 99.99 via Credit Card
cart.SetPaymentMethod(new PayPalPayment());
cart.Checkout(49.99m); // Paid 49.99 via PayPalThe Strategy pattern eliminates complex conditional logic. Adding a new payment method means creating a new class that implements IPaymentStrategy - no changes needed in ShoppingCart. This makes your code open for extension but closed for modification.
Challenge
EasyLet's build a shipping cost calculator using the Strategy pattern. Different shipping methods have different pricing algorithms, and your system should allow switching between them at runtime without changing the core order processing logic.
You'll organize your code across three files:
ShippingStrategy.cs: Define anIShippingStrategyinterface in theShippingnamespace with a methodCalculateCost(double weight)that returns adouble. Then create three concrete strategy classes:StandardShipping- calculates cost asweight * 1.5ExpressShipping- calculates cost asweight * 3.0 + 5.0(includes a flat express fee)OvernightShipping- calculates cost asweight * 5.0 + 10.0(premium service with higher base fee)
OrderProcessor.cs: Create anOrderProcessorclass in the same namespace. This is your context class that uses a shipping strategy. It should have:- A private field to hold the current
IShippingStrategy - A
SetShippingMethod(IShippingStrategy strategy)method to change the shipping strategy - A
ProcessOrder(double weight)method that uses the current strategy to calculate and return the shipping cost
- A private field to hold the current
Program.cs: Bring everything together by creating anOrderProcessorand demonstrating how you can swap shipping strategies at runtime. The same processor can calculate costs using different algorithms simply by changing its strategy.
You will receive two inputs:
- The shipping method type:
standard,express, orovernight - The package weight as a decimal number (e.g.,
4.5)
Create an OrderProcessor, set the appropriate shipping strategy based on the input type, then process the order and print the calculated cost.
For example, if the inputs are express and 4.5, the output should be:
18.5This demonstrates the Strategy pattern's power - your OrderProcessor doesn't contain any shipping calculation logic itself. It simply delegates to whatever strategy it's been given, making it easy to add new shipping methods (like DroneDelivery) without modifying the processor at all!
Cheat sheet
The Strategy pattern is a behavioral pattern that defines a family of algorithms, encapsulates each one in its own class, and makes them interchangeable at runtime.
The pattern consists of three parts:
- Strategy interface: defines the algorithm contract
- Concrete strategy classes: implement different behaviors
- Context class: uses a strategy without knowing its implementation details
Example structure:
// Strategy interface
public interface IPaymentStrategy
{
void Pay(decimal amount);
}
// Concrete strategies
public class CreditCardPayment : IPaymentStrategy
{
public void Pay(decimal amount) =>
Console.WriteLine($"Paid {amount} via Credit Card");
}
public class PayPalPayment : IPaymentStrategy
{
public void Pay(decimal amount) =>
Console.WriteLine($"Paid {amount} via PayPal");
}
// Context class
public class ShoppingCart
{
private IPaymentStrategy _paymentStrategy;
public void SetPaymentMethod(IPaymentStrategy strategy)
{
_paymentStrategy = strategy;
}
public void Checkout(decimal total)
{
_paymentStrategy.Pay(total);
}
}Usage - switching strategies at runtime:
var cart = new ShoppingCart();
cart.SetPaymentMethod(new CreditCardPayment());
cart.Checkout(99.99m); // Paid 99.99 via Credit Card
cart.SetPaymentMethod(new PayPalPayment());
cart.Checkout(49.99m); // Paid 49.99 via PayPalBenefits:
- Eliminates complex conditional logic
- Open for extension, closed for modification
- Adding new strategies requires only creating new classes, no changes to existing code
Try it yourself
using System;
using Shipping;
class Program
{
public static void Main(string[] args)
{
// Read input
string shippingType = Console.ReadLine();
double weight = Convert.ToDouble(Console.ReadLine());
// TODO: Create an OrderProcessor instance
// TODO: Based on shippingType ("standard", "express", or "overnight"),
// create the appropriate shipping strategy and set it on the processor
// TODO: Process the order and print the calculated cost
}
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Fundamentals of OOP
External FilesNamespaces & DirectivesIntro to Classes & ObjectsThe 'this' KeywordMethods and ParametersFields vs PropertiesConstructorsObject InitializersRecap - Simple Calculator4Inheritance
Basic Inheritance (:) SyntaxThe 'base' KeywordVirtual & Override KeywordsSealed ClassesThe 'object' Base ClassRecap - Employee Hierarchy7Advanced Features
Operator OverloadingIndexers (this[])ToString() OverrideExtension MethodsRecap - Custom List10Design Patterns Part 1
Intro to Design PatternsThread-Safe SingletonFactory PatternObserver Pattern (Events)Strategy Pattern2Properties & Static Members
Auto-Implemented PropertiesRead/Write-Only PropertiesStatic Fields & MethodsStatic ClassesExpression-Bodied Members5Polymorphism & Interfaces
Compile vs Runtime PolyInterface vs Abstract ClassMultiple InterfacesExplicit InterfacesUpcasting & DowncastingRecap - Shape Calculator