Strategy
Strategy encapsulates interchangeable algorithms behind a shared interface so behavior can be selected and swapped at runtime. It replaces sprawling conditionals with small, testable classes or functions and keeps the calling code stable as variants grow.
Strategy is a behavioral design pattern that lets you define a family of algorithms, put each one in a separate class, and make their objects interchangeable through a shared interface. The classic problem it solves is a class that needs to perform a task in several different ways, where the choice of approach changes over time or by context. Without Strategy, that variation tends to collect as a growing chain of conditional statements that is hard to read, test, and extend.
How It Works
The pattern has three roles. The strategy interface declares the operation common to all variants, for example calculate(order). Each concrete strategy implements that interface with one algorithm. The context holds a reference to a strategy object and delegates the work to it rather than implementing the logic itself. Clients configure the context with whichever strategy they want, usually by constructor injection or a setter, and can swap it at runtime.
Because the context depends only on the interface, it never needs to know which concrete algorithm it holds. Adding a new variant means writing one new class, not editing existing code. Many languages express Strategy with first-class functions or lambdas instead of full classes, which removes most of the boilerplate while keeping the same separation of concerns.
When to Use It
Reach for Strategy when you have several related algorithms that differ only in behavior, such as pricing rules, compression schemes, routing policies, or sorting comparators. It also fits when a class is cluttered with conditionals selecting behavior, or when you want to isolate algorithm details and their data from the code that uses them. It is a strong default whenever business rules are likely to grow or be configured per tenant, per region, or per experiment.
Trade-offs
Strategy increases the number of objects and indirection in the system, which can be overkill when there are only two stable variants and a simple conditional would do. Clients must be aware that strategies exist in order to choose one, so the selection logic moves rather than disappears; factories or dependency injection often handle that. In languages with lambdas, prefer functions over classes to avoid ceremony. Finally, strategies should be stateless or carefully scoped, since a shared mutable strategy can introduce concurrency bugs.
Related Patterns
Strategy is often confused with State, which also swaps behavior through an interface, but State transitions are driven by the object's internal condition while Strategy is chosen by the client. Template Method captures variation through inheritance and overridable steps rather than composition, so it fixes the variation at compile time. Command encapsulates a request as an object and pairs well with Strategy when the chosen algorithm must be queued, logged, or undone. Factory Method or a registry frequently supplies the concrete strategy, separating the decision of which algorithm to use from the code that uses it. Dependency injection containers are a common production mechanism for wiring the right strategy into the context.
Example
A payment service supports cards, wallets, and bank transfers. Define a PaymentStrategy interface with pay(amount). Implement CardPayment, WalletPayment, and BankTransfer, each owning its own validation, fees, and gateway calls. The checkout context receives the strategy that matches the customer's selection and calls pay, knowing nothing about the specifics. Supporting a new method later means adding one class and registering it, with no change to the checkout flow. The same shape applies far beyond payments: a compression utility selects gzip, brotli, or zstd; a route planner picks shortest, cheapest, or fastest; a tax engine chooses a jurisdiction-specific calculator. In every case the calling code stays stable while the catalogue of behaviors grows, and each strategy can be unit-tested in complete isolation from the others.