Strategy — vad är det, mönster strategi i iOS och Android

Författare: IT Sectr Publicerad: 2026-02-18 Lästid: 9 min

Strategy (Strategi) — ett beteendedesignmönster som definierar en familj av utbytbara algoritmer och placerar var och en av dem i en separat klass (Strategy). Mönstret gör det möjligt att välja algoritm i farten: klientkoden fungerar genom ett gemensamt Strategy-gränssnitt och den konkreta implementeringen sätts in vid körning. I iOS implementeras mönstret via Protocol + strategiklasser, i Android — via Interface + implementeringar. Strategy — ett av 23 GoF-mönster, allmänt använt för betalningshantering, validering, sortering och filtrering av data. Mer — i den ursprungliga GoF-beskrivningen.

Viktigast

  • Strategy — beteendemönster från GoF för en familj av utbytbara algoritmer
  • Inkapsling av algoritmer — varje algoritm är isolerad i sin egen klass
  • Strategy-gränssnitt — det gemensamma kontrakt som alla konkreta strategier implementerar
  • Komposition istället för arv — kontexten lagrar en referens till strategin, ärver inte beteende
  • Open/Closed-principen — nya strategier läggs till utan att ändra befintlig kod

Vad är Strategy-mönstret: essens och struktur

Strategy — ett av 23 GoF-mönster (Gang of Four), beskrivet i boken «Design Patterns: Elements of Reusable Object-Oriented Software» (1994). Mönstret löser problemet med algoritmval vid körning. Istället för att skriva en klass med många villkorsoperatorer (if-else, switch) föreslår Strategy att varje algoritm separeras i en separat klass med ett gemensamt gränssnitt. Kontexten (klassen som använder strategin) lagrar en referens till Strategy-gränssnittet och delegerar utförandet till den konkreta strategin.

Strukturen för mönstret omfattar tre element: Context (kontext) innehåller en referens till Strategy och anropar dess metod; Strategy (gränssnitt) deklarerar en gemensam metod för alla algoritmer; ConcreteStrategy (konkret strategi) implementerar gränssnittet och innehåller den konkreta algoritmen. Klienten skapar den önskade strategin och skickar den till kontexten via konstruktor, setter eller metodparameter. Kontexten vet inte vilken strategi som körs — den arbetar bara med gränssnittet.

KomponentRollExempel
ContextInnehåller en referens till StrategyPaymentProcessor, Sorter
StrategyGemensamt gränssnitt för algoritmerProtocol PaymentStrategy
ConcreteStrategyKonkret implementering av algoritmenCardPayment, PayPalPayment

Open/Closed-principen — den främsta fördelen med Strategy. Systemet är öppet för utvidgning (en ny strategi kan läggas till) och stängt för ändring (kontextkoden behöver inte ändras). Utan mönstret kräver tillägg av en ny algoritm ändring av den befintliga klassen, vilket bryter mot OCP och ökar risken för regressionsfel. Strategy minskar också klassernas storlek: istället för en 200-radig klass med switch-case får du 6 klasser med 20 rader var.

Strategy i iOS: implementering i Swift med Protocol

Strategy i Swift implementeras via Protocol (strategigränssnitt) och klasser eller strukturstrategier. Swift-protokoll stöder associated types och generic constraints, vilket ger flexibilitet vid design av strategier. Kontexten är vanligtvis en ViewModel-klass eller tjänst som tar emot strategin i init eller via en egenskap. Mönstret används allmänt i iOS-projekt för händelsehantering, animationer, dataformatering och UI-strategier.

swift
// 1. Protocol Strategy
protocol PaymentStrategy {
    func pay(amount: Decimal) async throws -> PaymentResult
}

// 2. Konkreta strategier
struct CardPaymentStrategy: PaymentStrategy {
    let cardNumber: String
    let cvv: String

    func pay(amount: Decimal) async throws -> PaymentResult {
        // Skicka begäran till bank-API
        return PaymentResult(status: .success, transactionId: "tx_\(UUID())")
    }
}

struct PayPalPaymentStrategy: PaymentStrategy {
    let email: String

    func pay(amount: Decimal) async throws -> PaymentResult {
        // Omdirigering till PayPal SDK
        return PaymentResult(status: .success, transactionId: "pp_\(UUID())")
    }
}

// 3. Context
class PaymentProcessor {
    private var strategy: PaymentStrategy

    init(strategy: PaymentStrategy) {
        self.strategy = strategy
    }

    func setStrategy(_: PaymentStrategy) {
        strategy = strategy
    }

    func processPayment(amount: Decimal) async throws -> PaymentResult {
        return try await strategy.pay(amount: amount)
    }
}

// Användning
let processor = PaymentProcessor(strategy: CardPaymentStrategy(cardNumber: "4111...", cvv: "123"))
let result = try await processor.processPayment(amount: 99.99)

Strategy i SwiftUI — mönstret integreras naturligt med MVVM. ViewModel innehåller en strategiegenskap och anropar dess metod vid användaråtgärd. SwiftUI View tar emot data via @Published eller @State — strategin döljer implementeringsdetaljer för View. Till exempel byts textvalideringsstrategin (emailValidator, phoneValidator) beroende på inmatningsfältets typ. Kombinationen Strategy med SwiftUI ger flexibilitet utan arv från UIKit.

Strategy i Android: implementering i Kotlin med Interface

Strategy i Kotlin använder Interface på språknivå och funktionella gränssnitt (SAM) för förenkling. Kotlin stöder lambdas, vilket gör det möjligt att skicka algoritmer som funktioner utan att deklarera en separat strategiklass. I Android tillämpas mönstret i ViewModel och Use Cases för isolering av algoritmer för dataladdning, cachning och felhantering. Android-projekt med Clean Architecture använder Strategy för att injicera olika repository-implementeringar beroende på flaggor (mock, real, cache).

kotlin
// 1. Interface Strategy
interface PaymentStrategy {
    suspend fun pay(amount: BigDecimal): PaymentResult
}

// 2. Konkreta strategier
class CardPaymentStrategy(
    private val cardNumber: String,
    private val cvv: String
) : PaymentStrategy {
    override suspend fun pay(amount: BigDecimal): PaymentResult {
        // Bank-API via Retrofit
        return PaymentResult(success = true, transactionId = "tx_${UUID.randomUUID()}")
    }
}

class PayPalPaymentStrategy(
    private val email: String
) : PaymentStrategy {
    override suspend fun pay(amount: BigDecimal): PaymentResult {
        // PayPal SDK integration
        return PaymentResult(success = true, transactionId = "pp_${UUID.randomUUID()}")
    }
}

// 3. Context
class PaymentProcessor(
    private val strategy: PaymentStrategy
) {
    fun setStrategy(strategy: PaymentStrategy): PaymentProcessor {
        return PaymentProcessor(strategy)
    }

    suspend fun processPayment(amount: BigDecimal): PaymentResult {
        return strategy.pay(amount)
    }
}

// Användning i ViewModel
class CheckoutViewModel : ViewModel() {
    private var processor = PaymentProcessor(CardPaymentStrategy("4111...", "123"))

    fun payWithCard() {
        viewModelScope.launch {
            val result = processor.processPayment(BigDecimal("99.99"))
            // Bearbetning av resultat
        }
    }
}

Strategy med Hilt/Dagger — i Android-projekt injiceras strategier ofta via DI. Hilt tillhandahåller den konkreta implementeringen av PaymentStrategy via @Binds eller @Provides. Detta gör det möjligt att ändra strategi utan att ändra kontextkoden — det räcker med att ändra DI-modulen för en annan build (debug/release). Till exempel injiceras MockPaymentStrategy för felsökning och den verkliga bankstrategin för produktion. Kombinationen Strategy + DI ger maximal flexibilitet.

Jämförelse av Strategy med State, Command och Template Method

Strategy vs State — strukturellt är mönstren identiska: båda använder komposition med gränssnitt och konkreta klasser. Skillnaden ligger i syftet: Strategy väljer en oberoende algoritm, State hanterar objektets beteende beroende på dess tillstånd. I State ändrar kontexten själv strategin vid tillståndsändring, i Strategy kontrollerar kontexten inte växlingen — klienten specificerar explicit algoritmen. Strategier vet inte om varandra, tillstånd kan övergå i varandra.

Strategy vs Command — Command kapslar in en enskild åtgärd som ett objekt, Strategy kapslar in en uppsättning utbytbara algoritmer. Command — «vad ska göras» (ett execute-anrop), Strategy — «hur ska göras» (algoritm i flera steg). Command används för köer, fördröjd körning, undo/redo. Strategy — för att välja utförandesätt för en uppgift vid körning. Kommandon kan parametriseras med strategier, vilket kombinerar båda mönstren.

EgenskapStrategyStateCommandTemplate Method
SyfteUtbytbara algoritmerBeteende beroende på tillståndInkapsling av begäranAlgoritmskelett
ÄndringExplicit av klientenAutomatiskt av kontextenAv klient eller köGenom arv
NivåObjekt (komposition)Objekt (komposition)ObjektKlass (arv)

Strategy vs Template Method — båda mönstren definierar algoritmer, men på olika sätt. Template Method använder arv: basklassen definierar algoritmens skelett (mallmetod), underklasser åsidosätter enskilda steg. Strategy använder komposition: algoritmen är helt utbruten i en separat klass. Template Method är enklare för fall med fast algoritmstruktur, Strategy — när algoritmerna är helt olika och kan ändras dynamiskt.

Verkliga exempel på användning av Strategy-mönstret

Betalningshantering — det klassiska exemplet på Strategy. Varukorgen i en webbutik innehåller en produktlista och betalningssättet väljs av användaren. Varje sätt (kort, PayPal, Apple Pay, Google Pay, kryptovaluta) — en separat strategi med den gemensamma signaturen pay(amount). Kontexten PaymentProcessor vet inte exakt hur betalningen utförs — den anropar den gemensamma metoden. Att lägga till ett nytt betalningssätt kräver ingen ändring av varukorgskoden.

Datavalidering — Strategy tillämpas för olika valideringsregler för samma fält. EmailValidatorStrategy, PhoneValidatorStrategy, AgeValidatorStrategy implementerar det gemensamma gränssnittet ValidationStrategy med metoden validate(input). Registreringsformuläret använder en uppsättning strategier för att kontrollera varje fält. Valideringsstrategier kan kombineras i en kedja (Chain of Responsibility) eller tillämpas alla samtidigt i en loop. Detta ersätter långa if-else-kontroller med en samling polymorfa validerare.

swift
// Sorteringsstrategi
protocol SortingStrategy {
    func sort<T>(_ items: [T]) -> [T] where T: Comparable
}

struct QuickSortStrategy: SortingStrategy {
    func sort<T>(_ items: [T]) -> [T] { /* quicksort */ items }
}

struct MergeSortStrategy: SortingStrategy {
    func sort<T>(_ items: [T]) -> [T] { /* mergesort */ items }
}

class SortedDataSource<T> {
    private var strategy: SortingStrategy
    func display(_ items: [T]) { let sorted = strategy.sort(items) }
}

Autentisering — i mobila applikationer växlar autentiseringsstrategier beroende på leverantör. AuthStrategy med metoderna login(), logout(), getToken() implementeras för EmailPasswordAuth, GoogleAuth, AppleAuth, BiometricAuth. Kontexten AuthManager tar emot strategin via DI eller fabrik. Detta gör det möjligt att lägga till nya autentiseringsleverantörer utan att ändra inloggningsskärmen. Strategy-mönstret — grunden för många OAuth-bibliotek och Firebase Authentication.

Vanliga frågor

När ska Strategy användas istället för if-else?

Strategy är motiverat när du har 3+ algoritmer som kan ändras eller utökas. Om algoritmerna är 2 och stabila — är en enkel if-else mindre kostsam. Använd Strategy när algoritmer används i olika delar av applikationen, när algoritmer behöver bytas ut vid körning eller när varje algoritm kräver egna beroenden och tester.

Är Strategy samma sak som State?

Nej, det är olika mönster med liknande struktur. Strategy — klienten väljer explicit algoritmen och strategierna är oberoende. State — objektet ändrar själv sitt beteende vid förändring av inre tillstånd och tillstånd kan övergå i varandra. I State hanterar kontexten tillståndsändringen, i Strategy — klientkoden.

Kan Strategy användas utan klasser — via closures?

Ja, i Swift och Kotlin kan strategin skickas som en closure eller lambda. Swift: typealias PaymentHandler = (Decimal) async throws -> PaymentResult. Kotlin: typealias PaymentFun = suspend (BigDecimal) -> PaymentResult. Detta förenklar koden för enkla fall, men namngivning och dokumentation går förlorad. För 1-2 algoritmer — räcker closure, för 4+ — är separata klasser bättre.

Hur testar man Strategy-mönstret?

Varje strategi testas med ett separat enhetstest med mock-beroenden. Kontexten testas med en mock-strategi — det verifieras att kontexten anropar strategins metod och skickar korrekta parametrar. I Swift använd XCTest + protokoll för mock, i Kotlin — MockK eller Mockito. Den främsta fördelen: varje strategi testas isolerat utan komplex konfiguration.

Är Strategy ett GoF-mönster?

Ja, Strategy (Strategi) — ett av 23 mönster som beskrivs i boken «Design Patterns: Elements of Reusable Object-Oriented Software» (Gamma, Helm, Johnson, Vlissides, 1994). Det tillhör gruppen beteendemönster (Behavioral Patterns). Synonymer: Policy (Politik). Den ursprungliga exempelkoden i Smalltalk-80 finns i den ursprungliga GoF-utgåvan.

Sammanfattning

  • Strategy — beteendemönster för utbytbara algoritmer med gemensamt gränssnitt
  • Inkapsling — varje algoritm är isolerad i en separat strategiklass
  • iOS Swift — implementering via Protocol, strukturer och closures
  • Android Kotlin — implementering via Interface, lambdas och DI med Hilt
  • Open/Closed-principen — nya strategier läggs till utan att ändra kontexten
  • Användning — betalningar, validering, sortering, autentisering, formatering

Vi utvecklar en mobil applikation nyckelfärdigt

IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.

Diskutera projektet

Läs också