DEV Community

Cover image for Design Patterns by Purpose: The Strategy Pattern in Frontend Life (Part 3)
Anju Karanji
Anju Karanji

Posted on • Edited on

Design Patterns by Purpose: The Strategy Pattern in Frontend Life (Part 3)

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

trategy Pattern UML — BaseCat with StreetCat, HouseCat (necktie), Maneki-neko, and RoboCat. Behaviors via Eat/Sound/Move strategies.

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} 
/>
Enter fullscreen mode Exit fullscreen mode

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)