Wzorzec strategii - Koniec z if-else hell w JavaScript/TypeScript

Kod implementuje **strategy pattern**, gdzie różne funkcje obsługują dane w zależności od warunków, np. `htmlEncodeRegEx`.

Napisano przez

Jacek Zając

Opublikowano

27 kwi 2026

Spis treści

Wzorzec strategii pomaga uporządkować kod wtedy, gdy jedna operacja zaczyna mieć kilka wariantów, a każda kolejna reguła dokłada następny warunek. Ja patrzę na niego przede wszystkim jako na sposób na ograniczenie rozrastających się ifów, uproszczenie testów i zachowanie czytelnej architektury w aplikacji, która ma żyć dłużej niż jeden sprint. W tym tekście pokazuję, jak działa strategy pattern, kiedy naprawdę ma sens, jak wdrożyć go w JavaScript lub TypeScript oraz gdzie początkujący najczęściej przesadzają z abstrakcją.

Najważniejsze rzeczy, które warto wiedzieć o wzorcu strategii

  • Wzorzec strategii wydziela zmienne algorytmy do osobnych implementacji i pozwala je podmieniać bez ruszania kodu klienta.
  • Najlepiej sprawdza się tam, gdzie jedna decyzja biznesowa ma kilka wariantów, a liczba wyjątków rośnie z czasem.
  • W projektach webowych często stosuje się go do wyceny, walidacji, dostawy, sortowania, formatowania danych i reguł uprawnień.
  • W JavaScript i TypeScript strategią nie musi być klasa, często wystarczy funkcja albo obiekt z jedną metodą.
  • Jeśli masz tylko 2-3 stabilne gałęzie i nie przewidujesz rozbudowy, zwykły warunek bywa rozsądniejszy niż pełny wzorzec.
  • Największy zysk daje wtedy, gdy chcesz testować algorytmy osobno i wymieniać je bez naruszania reszty systemu.

Na czym polega wzorzec strategii

Strategy pattern to wzorzec behawioralny, w którym jeden kontekst korzysta z wielu wymiennych algorytmów, a każdy z nich robi tę samą rzecz w nieco inny sposób. Zamiast zamykać logikę w jednym dużym bloku warunkowym, wyciągam warianty do osobnych implementacji i podłączam je tam, gdzie są potrzebne. Dzięki temu kod klienta nie musi wiedzieć, jak działa algorytm, tylko który wariant został wybrany.

W praktyce składa się to z trzech elementów: kontekstu, który zleca wykonanie zadania, interfejsu strategii, który opisuje wspólne zachowanie, oraz konkretnych strategii, które zawierają różne wersje algorytmu. To podejście dobrze wspiera zasadę otwarte-zamknięte: łatwo dodaję nowy wariant, ale nie muszę modyfikować istniejącego kodu za każdym razem, gdy pojawia się kolejna reguła biznesowa.

Ja lubię ten wzorzec za to, że jest prosty koncepcyjnie, ale realnie porządkuje architekturę. Gdy już widzisz tę strukturę, naturalnie pojawia się pytanie: kiedy opłaca się ją wprowadzać, a kiedy lepiej zostać przy zwykłym warunku?

Kiedy lepiej użyć strategii niż kolejnych ifów

Nie każdy warunek trzeba od razu zamieniać w wzorzec projektowy. Jeżeli masz dwa stabilne warianty i żadnych sygnałów, że kod będzie się rozrastał, prosty if albo switch bywa czytelniejszy. Wzorzec strategii zaczyna mieć przewagę wtedy, gdy logika wyboru algorytmu przestaje być incydentalna, a staje się jednym z głównych punktów zmian w systemie.
Sygnał w kodzie Co to zwykle oznacza Co robię w praktyce
2-3 stabilne warianty Zmiana jest mała i raczej nie urośnie szybko Zostawiam prosty warunek
4+ warianty lub rosnące wyjątki Gałęzie zaczynają się dublować Wydzielam strategię
Różne zasady dla różnych klientów, planów lub krajów Algorytm zależy od kontekstu biznesowego Rozdzielam warianty na osobne implementacje
Potrzeba testów jednostkowych dla samego algorytmu Logika jest zbyt ściśle spleciona z resztą klasy Wyciągam ją do strategii
Wybór algorytmu może się zmieniać w runtime Nie wystarczy decyzja na etapie kompilacji Podpinam strategię dynamicznie

Właśnie tutaj najłatwiej odróżnić dobry wzorzec od sztucznej komplikacji. Jeśli decyzja ma się zmieniać, rosnąć lub być testowana osobno, strategia zwykle wygrywa. Jeśli nie, prosta gałąź pozostaje najzdrowszym wyborem. To prowadzi do pytania, jak zbudować taki układ w aplikacji bez tworzenia niepotrzebnej hierarchii.

Diagram sekwencji ilustrujący **strategy pattern**: Klient ustawia strategię (A lub B) w kontekście, który następnie wykonuje operację.

Jak to działa w aplikacji webowej

W aplikacjach webowych najczęściej myślę o tym wzorcu jako o osobnym komponencie odpowiedzialnym za jedną decyzję biznesową: obliczenie ceny, dobranie formy dostawy, wyliczenie podatku, walidację albo formatowanie wyniku. Kontekst przyjmuje dane wejściowe, przekazuje je do wybranej strategii i zwraca efekt, nie wiedząc nic o szczegółach algorytmu.

interface ShippingStrategy {
  calculate(price: number): number;
}

class StandardShipping implements ShippingStrategy {
  calculate(price: number) {
    return price < 200 ? 15 : 0;
  }
}

class ExpressShipping implements ShippingStrategy {
  calculate(price: number) {
    return price < 200 ? 29 : 19;
  }
}

class Checkout {
  private strategy: ShippingStrategy;

  constructor(strategy: ShippingStrategy) {
    this.strategy = strategy;
  }

  setStrategy(strategy: ShippingStrategy) {
    this.strategy = strategy;
  }

  total(price: number) {
    return price + this.strategy.calculate(price);
  }
}

Ten przykład jest prosty, ale dobrze pokazuje sedno: Checkout nie zawiera logiki wyceny dostawy. Może pracować z dowolną strategią, a nowy wariant, na przykład odbiór w punkcie, nie wymaga przepisywania całej klasy. W JavaScript ten sam pomysł można zapisać jeszcze lżej, jako funkcję lub obiekt z metodą, bez budowania ciężkiej warstwy klasowej.

To właśnie dlatego w frontendzie i backendzie webowym ten wzorzec bywa wyjątkowo praktyczny. Kiedy rozumiesz przepływ zależności, łatwiej też odróżnić go od innych mechanizmów, które z zewnątrz wyglądają podobnie, ale rozwiązują inny problem.

Strategy, stan i prosty polimorfizm to nie to samo

Ten wzorzec często myli się ze stanem albo z samym polimorfizmem, bo wszystkie trzy pojęcia krążą wokół wymiennego zachowania. Różnica jest jednak ważna. W strategii wybierasz algorytm, który realizuje to samo zadanie w różny sposób. W stanie zachowanie obiektu zmienia się dlatego, że sam obiekt przechodzi między fazami życia. Polimorfizm natomiast jest cechą języka, a nie samym wzorcem.

Mechanizm Kto decyduje o zmianie Na czym skupia się logika Przykład
Wzorzec strategii Kontekst lub warunek zewnętrzny Wybór algorytmu Różne sposoby liczenia rabatu
State pattern Bieżący stan obiektu Zmiana zachowania wraz z fazą życia Zamówienie: nowe, opłacone, wysłane
Polimorfizm Obiekt wywoływany przez interfejs Wspólna metoda w różnych klasach calculate() w kilku implementacjach
Zwykły switch Jednorazowy warunek w kodzie Bezpośredni wybór ścieżki Format eksportu: PDF, CSV, JSON

Ja zwykle upraszczam to do jednego pytania: czy wybieram sposób wykonania zadania, czy opisuję stan obiektu. Jeśli odpowiedź brzmi „sposób”, strategia jest bardzo mocnym kandydatem. Jeśli odpowiedź brzmi „stan”, lepiej patrzeć w stronę wzorca stanu. A skoro wiemy już, czego unikać na poziomie koncepcji, warto przejść do błędów, które najczęściej psują wdrożenie w praktyce.

Najczęstsze błędy przy wdrażaniu

Największy błąd, jaki widzę, to tworzenie osobnej klasy strategii dla każdej drobnej różnicy. Jeśli zmiana dotyczy jednego warunku i raczej nie będzie rosła, taki podział tylko zwiększa liczbę plików i obniża czytelność. Drugi problem to przenoszenie całej logiki wyboru do kontekstu, przez co zamiast uproszczenia dostajemy warstwę pośrednią, która nadal ma wielki blok decyzji.

  • Przedwczesna abstrakcja - strategia powstaje „na zapas”, choć kod mógł zostać prosty jeszcze przez długi czas.
  • Za dużo klas - każdy mikrowariant dostaje osobny plik, mimo że różnice są kosmetyczne.
  • Ukryta zależność od kontekstu - strategia zaczyna znać zbyt wiele szczegółów o klasie, która ją używa.
  • Brak spójnego kontraktu - każda implementacja robi coś trochę innego, więc wspólny interfejs przestaje mieć sens.
  • Mylenie wzorca z architekturą całej aplikacji - strategia rozwiązuje jeden problem, nie cały projekt.

W praktyce najlepszy filtr jest dość prosty: jeśli po dodaniu nowej strategii muszę ruszyć stary kod, to znaczy, że wzorzec został wdrożony nie do końca poprawnie. Dobrze zaprojektowana strategia pozwala dopisać nowy wariant obok istniejących, bez rozbijania całego układu. To z kolei prowadzi do pytania, gdzie ten wzorzec naprawdę daje najwięcej korzyści w projektach webowych.

Gdzie ten wzorzec daje największy zwrot w projektach webowych

W projektach webowych najczęściej stosuję ten wzorzec tam, gdzie jedna operacja ma kilka sensownych wariantów, a różnice są biznesowe, nie techniczne. To są miejsca, w których kod bardzo szybko się rozjeżdża, jeśli zostanie w jednym bloku warunkowym. Poniżej są scenariusze, które naprawdę często się opłacają.

Scenariusz Co się zmienia Dlaczego strategia pomaga
Wycena koszyka Rabaty, prowizje, podatki, waluty Każdą regułę można testować osobno
Opcje dostawy Kurier, odbiór w punkcie, ekspres Łatwo dodać nowy wariant bez przepisywania checkoutu
Walidacja formularza Reguły zależne od kraju, typu konta lub planu Reguły nie mieszają się w jednym warunku
Sortowanie i filtrowanie Kolejność, priorytety, wielokryterialność Nowy algorytm nie psuje starego
Generowanie raportów CSV, PDF, JSON, XLSX Format eksportu staje się wymiennym modułem
Uprawnienia i polityki dostępu Różne reguły dla ról i planów Bezpieczniej utrzymać wiele wariantów obok siebie

Właśnie w takich miejscach wzorzec przestaje być teorią z książki, a zaczyna realnie zmniejszać koszt zmian. Ja traktuję go jak narzędzie do porządkowania zmienności: nie używam go wszędzie, ale tam, gdzie logika biznesowa ma wyraźne warianty, daje bardzo dobrą relację prostoty do elastyczności. Z tego zostaje już tylko ostatnia, praktyczna decyzja: kiedy ten wybór jest opłacalny, a kiedy lepiej zostać przy prostszym kodzie.

Kiedy ta decyzja jest naprawdę opłacalna

Wzorzec strategii ma sens wtedy, gdy koszt utrzymania jednego dużego warunku jest większy niż koszt kilku małych implementacji. To brzmi banalnie, ale właśnie na tym polega dobra decyzja architektoniczna: nie na modzie, tylko na bilansie korzyści. Ja zwykle zadaję sobie trzy pytania. Czy wariantów będzie przybywać? Czy użytkownik albo biznes wybiera je dynamicznie? Czy każdą wersję chcę testować osobno?

  • Wybieram strategię, gdy mam co najmniej 3-4 warianty, a ich liczba prawdopodobnie wzrośnie.
  • Wybieram strategię, gdy algorytmy różnią się na tyle, że testowanie ich w jednym miejscu staje się uciążliwe.
  • Wybieram strategię, gdy chcę uniknąć kopiowania tej samej logiki w kilku klasach lub modułach.
  • Rezygnuję z niej, gdy problem jest mały, stabilny i nie ma sygnałów, że będzie się rozrastał.

Jeżeli patrzysz na własny kod i widzisz coraz dłuższy blok decyzyjny, to często znak, że warto odciążyć go strategią. Jeżeli widzisz tylko dwa proste warianty i żadnej presji na rozbudowę, prostsze rozwiązanie będzie lepsze. W praktyce właśnie ta umiejętność odróżnienia jednego przypadku od drugiego robi największą różnicę w jakości architektury.

FAQ - Najczęstsze pytania

Wzorzec strategii to wzorzec behawioralny, który pozwala na definiowanie rodziny algorytmów, hermetyzowanie każdego z nich i czynienie ich wymiennymi. Dzięki temu algorytm może zmieniać się niezależnie od klientów, którzy go używają, co porządkuje kod i ułatwia testowanie.

Warto go użyć, gdy masz 3-4 lub więcej wariantów danej operacji, które mogą się rozrastać, lub gdy algorytmy muszą być testowane niezależnie. Jeśli wariantów jest mało i są stabilne, prosty if/switch może być lepszym rozwiązaniem.

Najczęstsze błędy to przedwczesna abstrakcja, tworzenie zbyt wielu klas dla drobnych różnic, ukryta zależność strategii od kontekstu oraz brak spójnego kontraktu. Ważne jest, by strategia rozwiązywała realny problem, a nie komplikowała prosty kod.

Jest bardzo przydatny w scenariuszach takich jak wycena koszyka (rabaty, podatki), opcje dostawy, walidacja formularzy (reguły zależne od kontekstu), sortowanie i filtrowanie danych, generowanie raportów w różnych formatach oraz zarządzanie uprawnieniami.

Nie, choć wszystkie dotyczą wymiennego zachowania. Strategia skupia się na wyborze algorytmu do wykonania zadania. Wzorzec stanu zmienia zachowanie obiektu w zależności od jego wewnętrznego stanu. Polimorfizm to cecha języka pozwalająca na wspólne metody w różnych klasach.

Oceń artykuł

Ocena: 0.00 Liczba głosów: 0

Tagi:

strategy pattern wzorzec strategii javascript strategy pattern typescript kiedy używać wzorca strategii

Udostępnij artykuł

Jacek Zając

Jacek Zając

Nazywam się Jacek Zając i od dziewięciu lat zajmuję się programowaniem webowym. Moja przygoda z tą dziedziną zaczęła się od fascynacji tworzeniem stron internetowych, co szybko przerodziło się w pasję do nauczania innych. Lubię dzielić się wiedzą i pomagać osobom, które stawiają pierwsze kroki w programowaniu. Skupiam się na wyjaśnianiu złożonych zagadnień w przystępny sposób, aby każdy mógł zrozumieć podstawy i rozwijać swoje umiejętności. W moich artykułach poruszam różnorodne tematy związane z programowaniem webowym, od HTML i CSS po JavaScript i frameworki. Dokładam wszelkich starań, aby informacje, które prezentuję, były rzetelne, aktualne i łatwe do przyswojenia. Regularnie śledzę nowinki w branży, co pozwala mi na dostarczanie czytelnikom treści zgodnych z najnowszymi trendami. Wierzę, że dobrze zorganizowana wiedza to klucz do sukcesu w karierze programisty.

Napisz komentarz