Don't hardcode your algorithms. Encapsulate them into interchangeable components and switch them at runtime to build truly flexible and scalable software.
In traditional programming, you often end up with a single class that needs to handle multiple variations of a task. This leads to a massive, fragile switch or if-else block.
Every time you want to add a new algorithm, you must modify that class, risking the breakage of existing code. This violates the Open/Closed Principle.
function calculate(type, data) {
if (type === 'fast') {
// 50 lines of logic
} else if (type === 'cheap') {
// 50 lines of logic
} else if (type === 'secure') {
// 50 lines of logic
}
// This grows forever...
}The object that needs to perform a task but doesn't care about the details of the algorithm.
The contract that all strategies must follow. It ensures the Context can talk to any strategy.
Multiple classes that implement the interface with specific, unique logic for the task.
Master the implementation across different environments.
/**
* ADVANCED JAVA IMPLEMENTATION
* Using Strategy with Dependency Injection concepts
*/
// 1. The Strategy Interface
interface RouteStrategy {
String calculateRoute(String A, String B);
int getEstimatedTime();
}
// 2. Concrete Strategy: Driving
class RoadStrategy implements RouteStrategy {
public String calculateRoute(String A, String B) {
return "Calculating fastest road route from " + A + " to " + B;
}
public int getEstimatedTime() { return 30; }
}
// 3. Concrete Strategy: Walking
class WalkingStrategy implements RouteStrategy {
public String calculateRoute(String A, String B) {
return "Finding scenic walking paths from " + A + " to " + B;
}
public int getEstimatedTime() { return 120; }
}
// 4. Concrete Strategy: Public Transit
class TransitStrategy implements RouteStrategy {
public String calculateRoute(String A, String B) {
return "Checking bus and train schedules between " + A + " and " + B;
}
public int getEstimatedTime() { return 45; }
}
// 5. The Context Class
class Navigator {
private RouteStrategy strategy;
// Default strategy set via constructor
public Navigator(RouteStrategy strategy) {
this.strategy = strategy;
}
// Dynamic strategy switching (Runtime)
public void setStrategy(RouteStrategy strategy) {
this.strategy = strategy;
}
public void buildRoute(String start, String end) {
System.out.println("--- Navigation Report ---");
System.out.println(strategy.calculateRoute(start, end));
System.out.println("ETA: " + strategy.getEstimatedTime() + " minutes");
}
}
// 6. Usage
public class StrategyDemo {
public static void main(String[] args) {
Navigator googleMaps = new Navigator(new RoadStrategy());
googleMaps.buildRoute("New York", "Boston");
// User decides to walk instead
googleMaps.setStrategy(new WalkingStrategy());
googleMaps.buildRoute("Central Park", "Times Square");
}
}In JavaScript, you don't always need classes. Passing a function as an argument is a literal implementation of the Strategy Pattern!
Combine this pattern with a Factory to decide which strategy to load based on a configuration file or database setting.
When you have many versions of the same behavior (e.g., different compression types, sorting algorithms, or encryption methods).
When an algorithm uses data that the business logic shouldn't know about. Strategy hides that data inside the strategy classes.
If your code is drowning in if (type == X), Strategy is your life raft to clean, modular logic.
| Pattern | Mechanism | Flexibility |
|---|---|---|
| Strategy | Composition (Has-A) | High: Change behavior at runtime. |
| Template | Inheritance (Is-A) | Medium: Locked in at compile time. |