Extract an algorithm behind a common interface so it can be swapped at runtime without touching the caller.
A checkout function that does `if (method === 'card') ... else if (method === 'paypal') ... else if (method === 'crypto') ...` mixes the decision of *which* algorithm with the *use* of it. Every new payment method means editing that same growing conditional, and the branches are hard to test independently.
Each algorithm becomes a small object implementing one shared method, e.g. `execute(amount)`. The context class holds a reference to the currently selected strategy and simply calls it — swapping strategies means swapping the reference, not editing a conditional.
`Checkout` never asks what kind of strategy it holds — it just calls `execute`. New payment methods are new classes, not new branches.
const CardStrategy = {
execute(amount) {
console.log(`Charging card: $${amount.toFixed(2)}`);
},
};
const CryptoStrategy = {
execute(amount) {
console.log(`Sending crypto invoice for $${amount.toFixed(2)}`);
},
};
class Checkout {
#strategy;
setStrategy(strategy) {
this.#strategy = strategy;
}
pay(amount) {
if (!this.#strategy) throw new Error("No payment strategy selected");
this.#strategy.execute(amount);
}
}
const checkout = new Checkout();
checkout.setStrategy(CardStrategy);
checkout.pay(42.5);
checkout.setStrategy(CryptoStrategy);
checkout.pay(120);