You've tamed structure (Module) and domesticated creation (Factory). Next up: behavior.
Consider a payment processor that needs different personalities (credit card, PayPal, crypto, bank transfer, gift card). Left alone, it turns into an if/else hydra. The Strategy Pattern trims that chaos: you define a small, stable interface for the operation (say process, validate, or charge) and pass in interchangeable implementations at runtime. Call sites stay consistent; only the strategy changes. Result: fewer branches, cleaner tests, and new variants without touching the core. Swap, don't sprawl.
Cat Behaviors: The Furry Problem
Meet Cat, our base class.
Now the variants: StreetCat, HouseCat, ManekiNeko, HelloKitty (famously… silent).
Hard-coding eat(), meow(), and move() into Cat breaks half the family—some behave differently, some don’t meow at all. Copy-pasting those methods into every subclass? Hairball city.
Enter Strategy: pull -> eat, sound, and move into small, swappable objects with the same interface. Compose each cat with the behaviors it actually needs at runtime. New cat? Meet RoboCat—USB-C-powered, doesn’t eat, outputs a synthetic meow, and yes, it flies. You just plug in eatBehavior: none, soundBehavior: synth, and moveBehavior: thrusters. No base-class surgery, no if/else hairballs—just composition that feels like a firmware upgrade.
A real-world pass: CollapsiblePanel with pluggable behaviors
Time to leave the cat café and touch UI. The panel’s structure stays boring on purpose; the behavior gets swapped in.
You ship a solid CollapsiblePanel, and suddenly it’s everywhere. Marketing wants color + sparkle on click. Settings wants non-clickable panels. The dashboard wants panels sorted by priority. Sound familiar? This is where the Strategy Pattern earns its keep: don’t fork your component or stuff it with if/else. Make it behavior-agnostic and accept tiny “strategies” for clicks, sorting—whatever you need.
Think of it this way: the panel is the car; strategies are the drivers. The car exposes the wheel and pedals; the driver decides how to drive. Your panel exposes markup and state; strategies decide what to do.
You can create small strategy functions for different behaviors:
// Strategy objects const clickStrategies = { colorSparkle: { onClick: (id) => addSparkleEffect(id), getStyle: (id) => ({ backgroundColor: 'lightblue', transition: 'all 0.3s' }) }, disabled: { onClick: () => {}, // No-op getStyle: () => ({ opacity: 0.6, pointerEvents: 'none' }) } }; const sortStrategies = { priority: (panels) => [...panels].sort((a, b) => a.priority - b.priority), alphabetical: (panels) => [...panels].sort((a, b) => a.title.localeCompare(b.title)), }; // The panel stays behavior-agnostic const CollapsiblePanel = ({ id, title, children, clickStrategy = clickStrategies.disabled }) => { return ( <div> <div onClick={() => clickStrategy.onClick(id)} style={clickStrategy.getStyle(id)} className="panel-header" > {title} </div> <div className="panel-content">{children}</div> </div> ); }; // Usage <CollapsiblePanel id="marketing" title="Campaign Results" clickStrategy={clickStrategies.colorSparkle} /> <CollapsiblePanel id="dashboard" title="Web Traffic" clickStrategy={clickStrategies.disabled} />
Before You If/Else
When you feel an if (type === 'special') coming on, pause. Ask: “Could this be a strategy instead?” If yes, extract a tiny function, pass it in, and keep your component calm. Future-you (and your team) will thank you when requirements change again next sprint.
Gotchas
No default strategy? Give your component a safe fallback (e.g., disabled behavior) so it never crashes when someone forgets to pass one in.
Testing panic? Don’t test every combo :)
Over-strategizing? Not every if deserves a strategy. A quick inline check is fine when there are only one or two cases and they won’t grow - extract them when you actually need the third variation.
When NOT to use Strategy:
- Simple toggles (theme.dark ? 'dark' : 'light')
- One-off customizations that won't grow
- Performance-critical paths (strategy dispatch has overhead)
When Strategy shines:
- Multiple implementations of the same interface
- Runtime behavior switching
- Plugin architectures
- Algorithm families that need to be swappable
Final Thoughts
Strategy is the gateway drug to plugin architectures. Mastering this helps you build micro-frontends, extensible design systems, and even frameworks that don't lock you into their opinions. Your components become platforms, not just UI blocks.
Posting in case it helps someone.
Top comments (0)