Strategy — mi ez, a stratégia minta iOS-ben és Android-ban

Szerző: IT Sectr Megjelenés: 2026-02-18 Olvasási idő: 9 perc

Strategy (Stratégia) — egy viselkedési tervezési minta, amely meghatározza a felcserélhető algoritmusok családját, és mindegyiket egy külön osztályba (Strategy) helyezi. A minta lehetővé teszi az algoritmus menet közbeni kiválasztását: a klienskód egy közös Strategy interfészen keresztül működik, a konkrét implementáció pedig futásidőben kerül beillesztésre. iOS-ben a minta Protocol + stratégia osztályokon keresztül, Android-ban — Interface + implementációkon keresztül valósul meg. A Strategy — a 23 GoF minta egyike, széles körben alkalmazzák fizetések feldolgozására, validálásra, rendezésre és adatszűrésre. Bővebben — a GoF eredeti leírásában.

Lényeg

  • Strategy — a GoF viselkedési mintája felcserélhető algoritmusok családjához
  • Algoritmusok beágyazása — minden algoritmus saját osztályában van elkülönítve
  • Strategy interfész — a közös szerződés, amelyet az összes konkrét stratégia implementál
  • Kompozíció az öröklés helyett — a kontextus tárol egy referenciát a stratégiára, nem örökli a viselkedést
  • Open/Closed elv — új stratégiák a meglévő kód módosítása nélkül adhatók hozzá

Mi a Strategy minta: lényeg és szerkezet

Strategy — a 23 GoF (Gang of Four) minta egyike, amelyet a «Design Patterns: Elements of Reusable Object-Oriented Software» (1994) könyv ír le. A minta megoldja az algoritmus futásidőben történő kiválasztásának problémáját. Ahelyett, hogy egy osztályt írnánk sok feltételes operátorral (if-else, switch), a Strategy azt javasolja, hogy minden algoritmust külön osztályba helyezzünk egy közös interfésszel. A kontextus (a stratégiát használó osztály) tárol egy referenciát a Strategy interfészre, és delegálja a végrehajtást a konkrét stratégiának.

A minta szerkezete három elemet foglal magában: Context (kontextus) tartalmazza a Strategy referenciáját és meghívja annak metódusát; Strategy (interfész) deklarál egy közös metódust az összes algoritmus számára; ConcreteStrategy (konkrét stratégia) implementálja az interfészt és tartalmazza a konkrét algoritmust. A kliens létrehozza a szükséges stratégiát, és átadja a kontextusnak a konstruktoron, setteren vagy metódusparaméteren keresztül. A kontextus nem tudja, melyik stratégia fut — csak az interfésszel dolgozik.

KomponensSzerepPélda
ContextTartalmazza a Strategy referenciájátPaymentProcessor, Sorter
StrategyKözös interfész az algoritmusok számáraProtocol PaymentStrategy
ConcreteStrategyAz algoritmus konkrét implementációjaCardPayment, PayPalPayment

Open/Closed elv — a Strategy fő előnye. A rendszer nyitott a bővítésre (új stratégia adható hozzá) és zárt a módosításra (nem kell megváltoztatni a kontextus kódját). A minta nélkül egy új algoritmus hozzáadása a meglévő osztály módosítását igényli, ami megsérti az OCP-t és növeli a regressziós hibák kockázatát. A Strategy csökkenti az osztályok méretét is: egy 200 soros switch-case osztály helyett 6 darab 20 soros osztályt kapunk.

Strategy iOS-ben: implementáció Swift-ben Protocol segítségével

Strategy Swift-ben a Protocol (stratégia interfész) és osztályok vagy struktúra-stratégiák segítségével implementálódik. A Swift protokollok támogatják az associated types és generic constraints szolgáltatásokat, ami rugalmasságot biztosít a stratégiák tervezésében. A kontextus általában egy ViewModel osztály vagy szolgáltatás, amely a stratégiát az init-ben vagy egy tulajdonságon keresztül fogadja. A mintát széles körben használják iOS projektekben eseménykezelésre, animációkra, adatformázásra és UI stratégiákra.

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

// 2. Konkrét stratégiák
struct CardPaymentStrategy: PaymentStrategy {
    let cardNumber: String
    let cvv: String

    func pay(amount: Decimal) async throws -> PaymentResult {
        // Kérés küldése a banki API-nak
        return PaymentResult(status: .success, transactionId: "tx_\(UUID())")
    }
}

struct PayPalPaymentStrategy: PaymentStrategy {
    let email: String

    func pay(amount: Decimal) async throws -> PaymentResult {
        // Átirányítás a PayPal SDK-ra
        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)
    }
}

// Használat
let processor = PaymentProcessor(strategy: CardPaymentStrategy(cardNumber: "4111...", cvv: "123"))
let result = try await processor.processPayment(amount: 99.99)

Strategy SwiftUI-ban — a minta természetesen integrálódik az MVVM-mel. A ViewModel tartalmaz egy stratégia tulajdonságot, és meghívja annak metódusát a felhasználói műveletnél. A SwiftUI View @Published vagy @State segítségével kapja az adatokat — a stratégia elrejti az implementációs részleteket a View elől. Például a szövegvalidálási stratégia (emailValidator, phoneValidator) a beviteli mező típusától függően változik. A Strategy és a SwiftUI kombinációja rugalmasságot biztosít az UIKit-től való öröklés nélkül.

Strategy Android-ban: implementáció Kotlin-ban Interface segítségével

Strategy Kotlin-ban nyelvi szinten az Interface-t és funkcionális interfészeket (SAM) használja az egyszerűsítéshez. A Kotlin támogatja a lambdákat, ami lehetővé teszi algoritmusok függvényként való átadását külön stratégia osztály deklarálása nélkül. Android-ban a mintát a ViewModel és Use Cases osztályokban alkalmazzák az adatbetöltési, gyorsítótárazási és hibakezelési algoritmusok elkülönítésére. A Clean Architecture-t használó Android projektek a Strategy-t használják a repository különböző implementációinak befecskendezésére a flag-ektől függően (mock, real, cache).

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

// 2. Konkrét stratégiák
class CardPaymentStrategy(
    private val cardNumber: String,
    private val cvv: String
) : PaymentStrategy {
    override suspend fun pay(amount: BigDecimal): PaymentResult {
        // Banki API Retrofit segítségével
        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)
    }
}

// Használat ViewModel-ben
class CheckoutViewModel : ViewModel() {
    private var processor = PaymentProcessor(CardPaymentStrategy("4111...", "123"))

    fun payWithCard() {
        viewModelScope.launch {
            val result = processor.processPayment(BigDecimal("99.99"))
            // Eredmény feldolgozása
        }
    }
}

Strategy Hilt/Dagger segítségével — Android projektekben a stratégiák gyakran DI-n keresztül kerülnek befecskendezésre. A Hilt a PaymentStrategy konkrét implementációját biztosítja @Binds vagy @Provides segítségével. Ez lehetővé teszi a stratégia megváltoztatását a kontextus kódjának módosítása nélkül — elég megváltoztatni a DI modult egy másik build-hez (debug/release). Például hibakereséshez MockPaymentStrategy, éles környezetben pedig a valós banki stratégia kerül befecskendezésre. A Strategy + DI kombináció maximális rugalmasságot biztosít.

A Strategy összehasonlítása a State, Command és Template Method mintákkal

Strategy vs State — szerkezetileg a minták azonosak: mindkettő kompozíciót használ interfésszel és konkrét osztályokkal. A különbség a célban van: a Strategy független algoritmust választ, a State az objektum viselkedését kezeli az állapotától függően. A State-ben a kontextus maga változtatja meg a stratégiát az állapot változásakor, a Strategy-ben a kontextus nem irányítja a váltást — a kliens explicit módon adja meg az algoritmust. A stratégiák nem tudnak egymásról, az állapotok átléphetnek egymásba.

Strategy vs Command — a Command egyetlen műveletet ágyaz be objektumként, a Strategy felcserélhető algoritmusok halmazát ágyazza be. Command — «mit kell tenni» (egy execute hívás), Strategy — «hogyan kell tenni» (több lépésből álló algoritmus). A Command sorokhoz, késleltetett végrehajtáshoz, undo/redo-hoz használatos. A Strategy — a feladat végrehajtási módjának kiválasztásához futásidőben. A parancsok stratégiákkal paraméterezhetők, kombinálva mindkét mintát.

JellemzőStrategyStateCommandTemplate Method
CélFelcserélhető algoritmusokÁllapotfüggő viselkedésKérés beágyazásaAlgoritmus váz
VáltozásExplicit a kliens általAutomatikus a kontextus általA kliens vagy sor általÖrökléssel
SzintObjektum (kompozíció)Objektum (kompozíció)ObjektumOsztály (öröklés)

Strategy vs Template Method — mindkét minta algoritmusokat határoz meg, de eltérő módon. A Template Method öröklést használ: az alaposztály meghatározza az algoritmus vázát (sablon metódus), az alosztályok felülírják az egyes lépéseket. A Strategy kompozíciót használ: az algoritmus teljes egészében egy külön osztályba kerül. A Template Method egyszerűbb rögzített algoritmusszerkezetű esetekben, a Strategy — amikor az algoritmusok teljesen eltérőek és dinamikusan változhatnak.

Valós példák a Strategy minta használatára

Fizetések feldolgozása — a Strategy klasszikus példája. Az online áruház kosara terméklistát tartalmaz, a fizetési módot a felhasználó választja ki. Minden mód (kártya, PayPal, Apple Pay, Google Pay, kriptovaluta) — egy külön stratégia a pay(amount) közös aláírással. A PaymentProcessor kontextus nem tudja, hogyan történik pontosan a fizetés — meghívja a közös metódust. Új fizetési mód hozzáadása nem igényli a kosár kódjának módosítását.

Adatvalidálás — a Strategy alkalmazható ugyanazon mező különböző validálási szabályaira. Az EmailValidatorStrategy, PhoneValidatorStrategy, AgeValidatorStrategy implementálja a közös ValidationStrategy interfészt a validate(input) metódussal. A regisztrációs űrlap stratégiák halmazát használja az egyes mezők ellenőrzésére. A validálási stratégiák láncba (Chain of Responsibility) kapcsolhatók, vagy mind egyszerre alkalmazhatók egy ciklusban. Ez hosszú if-else ellenőrzéseket polimorf validátorok gyűjteményével váltja fel.

swift
// Rendezési stratégia
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) }
}

Hitelesítés — mobil alkalmazásokban a hitelesítési stratégiák a szolgáltatótól függően változnak. Az AuthStrategy a login(), logout(), getToken() metódusokkal implementálódik EmailPasswordAuth, GoogleAuth, AppleAuth, BiometricAuth számára. Az AuthManager kontextus DI-n vagy gyáron keresztül fogadja a stratégiát. Ez lehetővé teszi új hitelesítési szolgáltatók hozzáadását a bejelentkezési képernyő módosítása nélkül. A Strategy minta — alapja számos OAuth könyvtárnak és a Firebase Authentication-nek.

Gyakran Ismételt Kérdések

Mikor használjuk a Strategy-t if-else helyett?

A Strategy akkor indokolt, ha 3+ algoritmusa van, amelyek változhatnak vagy bővíthetők. Ha 2 algoritmus van és stabilak — az egyszerű if-else kevésbé költséges. Használja a Strategy-t, ha az algoritmusokat az alkalmazás különböző részeiben használják, ha futásidőben kell algoritmusokat cserélni, vagy ha minden algoritmus saját függőségeket és teszteket igényel.

A Strategy ugyanaz, mint a State?

Nem, ezek különböző minták hasonló szerkezettel. Strategy — a kliens explicit módon választja ki az algoritmust, és a stratégiák függetlenek. State — az objektum maga változtatja meg a viselkedését a belső állapot változásakor, és az állapotok átléphetnek egymásba. A State-ben a kontextus kezeli az állapotváltozást, a Strategy-ben — a klienskód.

Használható-e a Strategy osztályok nélkül — closures segítségével?

Igen, Swift-ben és Kotlin-ban a stratégia átadható closure-ként vagy lambdaként. Swift: typealias PaymentHandler = (Decimal) async throws -> PaymentResult. Kotlin: typealias PaymentFun = suspend (BigDecimal) -> PaymentResult. Ez egyszerűsíti a kódot egyszerű esetekben, de az elnevezés és dokumentáció elveszik. 1-2 algoritmushoz — a closure elegendő, 4+ esetén — jobbak a külön osztályok.

Hogyan teszteljük a Strategy mintát?

Minden stratégiát külön egységteszttel tesztelünk mock függőségekkel. A kontextust mock stratégiával teszteljük — ellenőrizzük, hogy a kontextus meghívja a stratégia metódusát és átadja a megfelelő paramétereket. Swift-ben használja az XCTest + protokollokat a mock-hoz, Kotlin-ban — MockK vagy Mockito. A fő előny: minden stratégia elkülönítve tesztelhető bonyolult konfiguráció nélkül.

A Strategy GoF minta?

Igen, a Strategy (Stratégia) — a 23 minta egyike, amelyet a «Design Patterns: Elements of Reusable Object-Oriented Software» (Gamma, Helm, Johnson, Vlissides, 1994) könyv ír le. A viselkedési minták (Behavioral Patterns) csoportjába tartozik. Szinonimák: Policy (Politika). Az eredeti példakód Smalltalk-80-ban elérhető a GoF eredeti kiadásában.

Összegzés

  • Strategy — viselkedési minta felcserélhető algoritmusokhoz közös interfésszel
  • Beágyazás — minden algoritmus egy külön stratégia osztályban van elkülönítve
  • iOS Swift — implementáció Protocol, struktúrák és closures segítségével
  • Android Kotlin — implementáció Interface, lambdák és DI Hilt segítségével
  • Open/Closed elv — új stratégiák a kontextus módosítása nélkül adhatók hozzá
  • Alkalmazás — fizetések, validálás, rendezés, hitelesítés, formázás

Kulcsrakész mobilalkalmazást fejlesztünk

Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.

Projekt megbeszélése

Olvassa el is