Strategy Pattern: Behavioral Injection
The **Strategy Pattern** (or Policy Pattern) enables a class to select an algorithm's behavior at runtime. It defines a family of algorithms, encapsulates each one, and makes them interchangeable, allowing the algorithm to vary independently from the clients that use it.
I. The Lambda Evolution
In modern Java, creating a full class hierarchy for every strategy is often overkill. Using **Functional Interfaces** and **Lambdas**, we can inject behavior directly into the context.
Concrete Example: Functional Tax Calculator
```java
public class CheckoutService {
// Strategy defined as a functional interface
private ToDoubleBiFunction<Double, String> taxStrategy;
public void setTaxStrategy(ToDoubleBiFunction<Double, String> strategy) {
this.taxStrategy = strategy;
}
public double calculateTotal(double amount, String country) {
double tax = taxStrategy.applyAsDouble(amount, country);
return amount + tax;
}
}
// Usage
service.setTaxStrategy((amt, c) -> c.equals("EU") ? amt * 0.21 : amt * 0.05);
```
II. The Enum Strategy Pattern
For a fixed set of algorithms, an **Enum** can act as both the registry and the implementation provider. This is highly performant and type-safe.
```java
public enum DiscountPolicy {
RETAIL {
@Override public double apply(double price) { return price; }
},
VIP {
@Override public double apply(double price) { return price * 0.80; }
},
LIQUIDATION {
@Override public double apply(double price) { return price * 0.50; }
};
public abstract double apply(double price);
}
```
III. Strategy vs. State
* **Strategy:** Behavior is pluggable and usually stateless. The client "uses" a strategy.
* **State:** Behavior depends on internal state transitions. The object "becomes" its state.
IV. Technical Integrity
1. **Avoid Strategy Proliferation:** Do not create a strategy for every minor `if` statement. The pattern is intended for substantial behavioral variations.
2. **Performance:** Functional dispatch via lambdas is highly optimized by the JVM (often inlined), but the indirection does have a minor cost compared to a static `if/else`. Use it when flexibility is required, not as a default for all logic.
3. **Context Passing:** A strategy often needs data from the `Context`. Prefer passing only the required data (as in the `TaxCalculator` above) rather than passing the whole `Context` object to avoid tight coupling.
---
**See Also:**
- [Design Patterns Hub](DesignPatternsHub) — Architectural index.
- [Factory Pattern](FactoryPattern) — Often used to select and return the correct Strategy.
- [Functional Programming in Java](JavaStreamsAndFunctionalProgramming) — Bedrock for lambda-based strategies.