Strategy (Strategi)
Introduktion
Strategy är ett beteendemönster som låter dig definiera en familj av algoritmer, kapsla in var och en av dem, och göra dem utbytbara vid körning.
Problem
Du bygger en navigationsapp för resenärer. Appen startade med att bara visa bilrutter, men snart vill användare ha rutter för kollektivtrafik, gång och cykling.
Varje ny algoritm gör din Navigator-klass större. Snart är den en röra av if/else-satser. Att ändra en algoritm riskerar att förstöra de andra, och att lägga till en ny gör koden svårare att underhålla.
Lösning
Strategy-mönstret extraherar varje algoritm till en egen klass — en strategi. Context-klassen (Navigator) håller en referens till en strategi och delegerar arbetet till den. Strategin kan bytas ut när som helst vid körning.
När ska Strategy användas?
- När du vill kunna byta algoritm i ett objekt vid runtime.
- När du har många liknande klasser som bara skiljer sig i hur de utför ett beteende.
- För att isolera komplex algoritmlogik från resten av koden.
Struktur
| Roll | Ansvar |
|---|---|
| Context | Håller en referens till en strategi; delegerar arbetet till den. |
| Strategy Interface | Definierar gränssnittet som alla konkreta strategier måste följa. |
| Concrete Strategy | Implementerar en specifik variant av algoritmen. |
Exempel — Navigationsapp (typescript)
Scenariot: En Navigator kan byta ruttalgoritm vid körning utan att klientkoden ändras.
// ── Strategy Interface ─────────────────────────────────────
interface RouteStrategy {
buildRoute(origin: string, destination: string): void;
}
// ── Concrete Strategies ───────────────────────────────────
class RoadStrategy implements RouteStrategy {
buildRoute(origin: string, destination: string): void {
console.log(`Bilrutt: ${origin} → ${destination} (via motorväg)`);
}
}
class PublicTransportStrategy implements RouteStrategy {
buildRoute(origin: string, destination: string): void {
console.log(`Kollektivtrafik: ${origin} → ${destination} (tunnelbana + buss)`);
}
}
class WalkingStrategy implements RouteStrategy {
buildRoute(origin: string, destination: string): void {
console.log(`Gångrutt: ${origin} → ${destination} (via park)`);
}
}
// ── Context ───────────────────────────────────────────────
class Navigator {
constructor(private strategy: RouteStrategy) {}
setStrategy(strategy: RouteStrategy): void {
this.strategy = strategy;
}
buildRoute(origin: string, destination: string): void {
this.strategy.buildRoute(origin, destination);
}
}
// ── Klientkod ─────────────────────────────────────────────
const navigator = new Navigator(new RoadStrategy());
navigator.buildRoute("Stockholm", "Göteborg"); // Bilrutt
navigator.setStrategy(new PublicTransportStrategy());
navigator.buildRoute("Stockholm", "Göteborg"); // Kollektivtrafik
navigator.setStrategy(new WalkingStrategy());
navigator.buildRoute("Stortorget", "Centralstationen"); // Gång
Output:
Bilrutt: Stockholm → Göteborg (via motorväg)
Kollektivtrafik: Stockholm → Göteborg (tunnelbana + buss)
Gångrutt: Stortorget → Centralstationen (via park)
Fördelar
- Du kan byta algoritm i ett objekt vid runtime utan att ändra klientkoden.
- Open/Closed Principle — Lägg till nya strategier utan att ändra context.
- Algoritmernas implementationsdetaljer är isolerade från resten av koden.
Nackdelar
- Om du bara har ett fåtal algoritmer som sällan ändras, är mönstret överkurs.
- Klienten måste känna till skillnaderna mellan strategier för att välja rätt.
Av Victor Hernandez från Bytebase.se