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

RollAnsvar
ContextHåller en referens till en strategi; delegerar arbetet till den.
Strategy InterfaceDefinierar gränssnittet som alla konkreta strategier måste följa.
Concrete StrategyImplementerar en specifik variant av algoritmen.

Exempel — Navigationsapp (java)

Scenariot: En Navigator kan byta ruttalgoritm vid körning utan att klientkoden ändras.

// ── Strategy Interface ─────────────────────────────────────

public interface RouteStrategy {
    void buildRoute(String origin, String destination);
}

// ── Concrete Strategies ───────────────────────────────────

public class RoadStrategy implements RouteStrategy {
    @Override
    public void buildRoute(String origin, String destination) {
        System.out.println("Bilrutt: " + origin + " → " + destination + " (via motorväg)");
    }
}

public class PublicTransportStrategy implements RouteStrategy {
    @Override
    public void buildRoute(String origin, String destination) {
        System.out.println("Kollektivtrafik: " + origin + " → " + destination + " (tunnelbana + buss)");
    }
}

public class WalkingStrategy implements RouteStrategy {
    @Override
    public void buildRoute(String origin, String destination) {
        System.out.println("Gångrutt: " + origin + " → " + destination + " (via park)");
    }
}

// ── Context ───────────────────────────────────────────────

public class Navigator {
    private RouteStrategy strategy;

    public Navigator(RouteStrategy strategy) {
        this.strategy = strategy;
    }

    public void setStrategy(RouteStrategy strategy) {
        this.strategy = strategy;
    }

    public void buildRoute(String origin, String destination) {
        strategy.buildRoute(origin, destination);
    }
}

// ── Klientkod ─────────────────────────────────────────────

public class Application {
    public static void main(String[] args) {
        Navigator 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