Strategy — шта је то, образац стратегије у iOS и Android

Аутор: IT Sectr Објављено: 2026-02-18 Време читања: 9 мин

Strategy (Стратегија) — бихејвиорални образац пројектовања који дефинише породицу заменљивих алгоритама и смешта сваки од њих у посебну класу (Strategy). Образац омогућава избор алгоритма у лету: клијентски код ради преко заједничког интерфејса Strategy, а конкретна имплементација се подмеће у рантајму. У iOS-у образац се имплементира кроз Protocol + класе-стратегије, у Android-у — кроз Interface + имплементације. Strategy — један од 23 GoF образаца, широко се примењује за обраду плаћања, валидацију, сортирање и филтрирање података. Више — у оригиналном опису GoF.

Главно

  • Strategy — бихејвиорални образац GoF за породицу заменљивих алгоритама
  • Инкапсулација алгоритама — сваки алгоритам је изолован у сопственој класи
  • Интерфејс Strategy — заједнички уговор који имплементирају све конкретне стратегије
  • Композиција уместо наслеђивања — контекст чува референцу на стратегију, не наслеђује понашање
  • Open/Closed принцип — нове стратегије се додају без измене постојећег кода

Шта је образац Strategy: суштина и структура

Strategy — један од 23 GoF (Gang of Four) образаца, описан у књизи «Design Patterns: Elements of Reusable Object-Oriented Software» (1994). Образац решава проблем избора алгоритма у рантајму. Уместо писања једне класе са мноштвом условних оператора (if-else, switch), Strategy предлаже издвајање сваког алгоритма у посебну класу са заједничким интерфејсом. Контекст (класа која користи стратегију) чува референцу на интерфејс Strategy и делегира извршење конкретној стратегији.

Структура обрасца укључује три елемента: Context (контекст) садржи референцу на Strategy и позива њен метод; Strategy (интерфејс) декларише заједнички метод за све алгоритме; ConcreteStrategy (конкретна стратегија) имплементира интерфејс и садржи конкретан алгоритам. Клијент креира потребну стратегију и прослеђује је контексту кроз конструктор, сетер или параметар метода. Контекст не зна која стратегија се извршава — ради само са интерфејсом.

КомпонентаУлогаПример
ContextСадржи референцу на StrategyPaymentProcessor, Sorter
StrategyЗаједнички интерфејс за алгоритмеProtocol PaymentStrategy
ConcreteStrategyКонкретна имплементација алгоритмаCardPayment, PayPalPayment

Open/Closed принцип — главна предност Strategy. Систем је отворен за проширење (може се додати нова стратегија) и затворен за измену (не треба мењати код контекста). Без обрасца додавање новог алгоритма захтева измену постојеће класе, што крши OCP и повећава ризик од регресивних грешака. Strategy такође смањује величину класа: уместо 200-редне класе са switch-case добија се 6 класа од по 20 редова.

Strategy у iOS-у: имплементација у Swift-у са Protocol

Strategy у Swift-у се имплементира кроз Protocol (интерфејс стратегије) и класе или структуре-стратегије. Swift протоколи подржавају associated types и generic constraints, што даје флексибилност при пројектовању стратегија. Контекст је обично класа ViewModel или сервис који прима стратегију у init или кроз својство. Образац се широко користи у iOS пројектима за обраду догађаја, анимација, форматирање података и UI стратегије.

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

// 2. Конкретне стратегије
struct CardPaymentStrategy: PaymentStrategy {
    let cardNumber: String
    let cvv: String

    func pay(amount: Decimal) async throws -> PaymentResult {
        // Слање захтева банкарском API-ју
        return PaymentResult(status: .success, transactionId: "tx_\(UUID())")
    }
}

struct PayPalPaymentStrategy: PaymentStrategy {
    let email: String

    func pay(amount: Decimal) async throws -> PaymentResult {
        // Преусмеравање на 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)
    }
}

// Коришћење
let processor = PaymentProcessor(strategy: CardPaymentStrategy(cardNumber: "4111...", cvv: "123"))
let result = try await processor.processPayment(amount: 99.99)

Strategy у SwiftUI — образац се природно интегрише са MVVM. ViewModel садржи својство стратегије и позива њен метод при акцији корисника. SwiftUI View добија податке кроз @Published или @State — стратегија скрива детаље имплементације од View. На пример, стратегија валидације текста (emailValidator, phoneValidator) се мења у зависности од типа поља уноса. Комбиновање Strategy са SwiftUI даје флексибилност без наслеђивања од UIKit-а.

Strategy у Android-у: имплементација у Kotlin-у са Interface

Strategy у Kotlin-у користи Interface на нивоу језика и функционалне интерфејсе (SAM) за поједностављење. Kotlin подржава ламбде, што омогућава прослеђивање алгоритама као функција без декларисања посебне класе стратегије. У Android-у образац се примењује у ViewModel и Use Cases за изолацију алгоритама учитавања података, кеширања и обраде грешака. Android пројекти са Clean Architecture користе Strategy за инјектовање различитих имплементација репозиторијума у зависности од флагова (mock, real, cache).

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

// 2. Конкретне стратегије
class CardPaymentStrategy(
    private val cardNumber: String,
    private val cvv: String
) : PaymentStrategy {
    override suspend fun pay(amount: BigDecimal): PaymentResult {
        // Банкарски API кроз 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)
    }
}

// Коришћење у ViewModel-у
class CheckoutViewModel : ViewModel() {
    private var processor = PaymentProcessor(CardPaymentStrategy("4111...", "123"))

    fun payWithCard() {
        viewModelScope.launch {
            val result = processor.processPayment(BigDecimal("99.99"))
            // Обрада резултата
        }
    }
}

Strategy са Hilt/Dagger — у Android пројектима стратегије се често инјектују кроз DI. Hilt обезбеђује конкретну имплементацију PaymentStrategy кроз @Binds или @Provides. Ово омогућава промену стратегије без измене кода контекста — довољно је променити DI модул за другу компилацију (debug/release). На пример, за отклањање грешака инјектује се MockPaymentStrategy, за продукцију — реална банкарска стратегија. Комбинација Strategy + DI даје максималну флексибилност.

Поређење Strategy са State, Command и Template Method

Strategy vs State — структурно су обрасци идентични: оба користе композицију са интерфејсом и конкретним класама. Разлика је у намени: Strategy бира независан алгоритам, State управља понашањем објекта у зависности од његовог стања. У State-у контекст сам мења стратегију при промени стања, у Strategy-у контекст не контролише пребацивање — клијент експлицитно задаје алгоритам. Стратегије не знају једна за другу, стања могу прелазити једна у друга.

Strategy vs Command — Command инкапсулира једну радњу као објекат, Strategy инкапсулира скуп заменљивих алгоритама. Command — «шта урадити» (један позив execute), Strategy — «како урадити» (алгоритам од више корака). Command се користи за редове, одложено извршење, undo/redo. Strategy — за избор начина извршења задатка у рантајму. Команде се могу параметризовати стратегијама, комбинујући оба обрасца.

КарактеристикаStrategyStateCommandTemplate Method
НаменаЗаменљиви алгоритмиПонашање од стањаИнкапсулација захтеваСкелет алгоритма
ПроменаЕксплицитно од клијентаАутоматски од контекстаОд клијента или редаНаслеђивањем
НивоОбјектни (композиција)Објектни (композиција)ОбјектниКласни (наслеђивање)

Strategy vs Template Method — оба обрасца дефинишу алгоритме, али на различите начине. Template Method користи наслеђивање: базна класа дефинише скелет алгоритма (шаблон метод), подкласе преписују појединачне кораке. Strategy користи композицију: алгоритам је у потпуности измештен у посебну класу. Template Method је једноставнији за случајеве са фиксном структуром алгоритма, Strategy — када су алгоритми потпуно различити и могу се динамички мењати.

Реални примери коришћења обрасца Strategy

Обрада плаћања — класичан пример Strategy. Корпа интернет продавнице садржи листу производа, а начин плаћања бира корисник. Сваки начин (картица, PayPal, Apple Pay, Google Pay, криптовалута) — посебна стратегија са заједничким потписом pay(amount). Контекст PaymentProcessor не зна како се тачно врши плаћање — позива заједнички метод. Додавање новог начина плаћања не захтева измену кода корпе.

Валидација података — Strategy се примењује за различита правила валидације истог поља. EmailValidatorStrategy, PhoneValidatorStrategy, AgeValidatorStrategy имплементирају заједнички интерфејс ValidationStrategy са методом validate(input). Формулар за регистрацију користи скуп стратегија за проверу сваког поља. Стратегије валидације се могу комбиновати у ланац (Chain of Responsibility) или примењивати све одједном у петљи. Ово замењује дугачке if-else провере колекцијом полиморфних валидатора.

swift
// Стратегија сортирања
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) }
}

Аутентификација — у мобилним апликацијама стратегије аутентификације се пребацују у зависности од провајдера. AuthStrategy са методама login(), logout(), getToken() се имплементира за EmailPasswordAuth, GoogleAuth, AppleAuth, BiometricAuth. Контекст AuthManager прима стратегију кроз DI или фабрику. Ово омогућава додавање нових провајдера аутентификације без измене екрана за пријаву. Образац Strategy — основа за многе OAuth библиотеке и Firebase Authentication.

Често постављана питања

Када користити Strategy уместо if-else?

Strategy је оправдан када имате 3+ алгоритма који се могу мењати или проширивати. Ако су алгоритма 2 и стабилни су — једноставан if-else је мање захтеван. Користите Strategy када се алгоритми користе у различитим деловима апликације, када треба подмењати алгоритме у рантајму или када сваки алгоритам захтева сопствене зависности и тестове.

Strategy — да ли је то исто што и State?

Не, то су различити обрасци са сличном структуром. Strategy — клијент експлицитно бира алгоритам, и стратегије су независне. State — објекат сам мења своје понашање при промени унутрашњег стања, и стања могу прелазити једна у друга. У State-у контекст управља променом стања, у Strategy-у — клијентски код.

Може ли се Strategy користити без класа — кроз closures?

Да, у Swift-у и Kotlin-у стратегија се може проследити као затварање или ламбда. Swift: typealias PaymentHandler = (Decimal) async throws -> PaymentResult. Kotlin: typealias PaymentFun = suspend (BigDecimal) -> PaymentResult. Ово поједностављује код за једноставне случајеве, али се губи именовање и документација. За 1-2 алгоритма — closure је довољан, за 4+ — боље су посебне класе.

Како тестирати образац Strategy?

Свака стратегија се тестира посебним unit-тестом са mock-зависностима. Контекст се тестира са mock-стратегијом — проверава се да контекст позива метод стратегије и прослеђује исправне параметре. У Swift-у користите XCTest + протоколе за mock, у Kotlin-у — MockK или Mockito. Главна предност: свака стратегија се тестира изоловано без сложене конфигурације.

Strategy — да ли је то GoF образац?

Да, Strategy (Стратегија) — један од 23 обрасца описаних у књизи «Design Patterns: Elements of Reusable Object-Oriented Software» (Gamma, Helm, Johnson, Vlissides, 1994). Припада групи бихејвиоралних образаца (Behavioral Patterns). Синоними: Policy (Политика). Оригинални код примера на Smalltalk-80 доступан је у оригиналном издању GoF-а.

Закључци

  • Strategy — бихејвиорални образац за заменљиве алгоритме са заједничким интерфејсом
  • Инкапсулација — сваки алгоритам је изолован у посебној класи-стратегији
  • iOS Swift — имплементација кроз Protocol, структуре и затварања
  • Android Kotlin — имплементација кроз Interface, ламбде и DI са Hilt
  • Open/Closed принцип — нове стратегије се додају без измене контекста
  • Примена — плаћања, валидација, сортирање, аутентификација, форматирање

Развићемо мобилну апликацију под кључ

IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.

Разговарајте о пројекту

Прочитајте такође