Kiedy używać Strategy, a kiedy Strategy + Factory?

https://copilot.microsoft.com/chats/EMe1TGXmJQNXbjW72sB2N

Praktyczny przewodnik z przykładową strukturą plików (WordPress‑friendly)

W świecie backendu często pojawia się problem:

„Mam kilka wersji algorytmu i chcę łatwo przełączać się między nimi — bez if‑ów, bez bałaganu i bez ryzyka popsucia starego kodu.”

To właśnie moment, w którym błyszczy duet:

  • Strategy — różne algorytmy pod wspólnym interfejsem
  • Factory — wybór właściwego algorytmu po nazwie

W tym wpisie pokazuję:

  • kiedy wystarczy sam Strategy,
  • kiedy konieczne jest Strategy + Factory,
  • jak wygląda praktyczna struktura plików,
  • jak to działa w API (np. Flask).

🔹 Strategy — kiedy wystarczy?

Wzorzec Strategy rozwiązuje jeden problem:

„Chcę mieć różne implementacje tego samego algorytmu.”

Przykład:

class BaseStrategy:
    def analyze(self, df):
        raise NotImplementedError

class StrategyV1(BaseStrategy):
    def analyze(self, df):
        ...

class StrategyV2(BaseStrategy):
    def analyze(self, df):
        ...

Użycie:

strategy = StrategyV2()
strategy.analyze(df)

✔ Kiedy Strategy jest OK?

  • masz 1–2 strategie,
  • wybór strategii jest na stałe w kodzie,
  • nie planujesz dynamicznego przełączania,
  • nie planujesz wersjonowania algorytmów.

❌ Kiedy Strategy zaczyna przeszkadzać?

  • gdy pojawia się 3, 5, 10 strategii,
  • gdy chcesz wersjonować algorytmy (v1, v2, v3),
  • gdy wybór strategii ma pochodzić z API / UI / bazy danych,
  • gdy chcesz dodawać nowe strategie bez dotykania starego kodu.

Wtedy Strategy samo nie wystarcza.

🔹 Strategy + Factory — kiedy to konieczne?

Strategy mówi jak wykonać algorytm. Factory mówi którą strategię wybrać.

To dwa różne problemy.

🔥 Kluczowa idea

Każda strategia ma własne name:

class StrategyV2(BaseStrategy):
    name = "strat_v2"

Rejestr przechowuje je w słowniku:

STRATEGY_REGISTRY = {
    "strat_v1": StrategyV1,
    "strat_v2": StrategyV2,
}

Fabryka wybiera strategię po nazwie:

strategy = get_strategy("strat_v2")

I to jest moment, w którym system staje się plug‑and‑play.

📁 Struktura plików

analysis/
    __init__.py
    base.py
    registry.py
    factory.py
    strategies/
        __init__.py
        strategia_podstawowa.py
        strategia_podstawowa_v2.py
        strategia_podstawowa_v3.py
        strategia_eksperymentalna.py

🔹 base.py — interfejs strategii

class BaseAnalysisStrategy:
    name: str

    def analyze(self, df, **params):
        raise NotImplementedError

🔹 registry.py — automatyczny rejestr strategii

STRATEGY_REGISTRY = {}

def register_strategy(cls):
    STRATEGY_REGISTRY[cls.name] = cls
    return cls

🔹 factory.py — wybór strategii po nazwie

from .registry import STRATEGY_REGISTRY

def get_strategy(name: str):
    return STRATEGY_REGISTRY[name]()

🔹 strategia_podstawowa_v2.py — przykładowa strategia

@register_strategy #wymagane do zarejestrowania strategii w słowniku STRATEGY_REGISTRY
class StrategiaPodstawowaV2(BaseAnalysisStrategy):
    name = "strat_pods_v2"

    def analyze(self, df, **params):
        ...

🔥 Co daje Strategy + Factory?

1. Dodajesz nową strategię → dodajesz nowy plik → koniec

strategies/
    strategia_podstawowa_v4.py

Nadajesz:

name = "strat_pods_v4"

I już możesz:

get_strategy("strat_pods_v4")

2. Zero if‑ów

Nie ma:

if typ == "v1":
    ...
elif typ == "v2":
    ...

Fabryka robi to za Ciebie.

3. Idealne do API (Flask, FastAPI)

Użytkownik wybiera strategię:

wybrana = request.form.get("user_strategy", "strat_pods_v1")
wynik = get_strategy(wybrana).analyze(df)

4. Idealne do wersjonowania algorytmów

  • strat_pods_v1
  • strat_pods_v2
  • strat_pods_v3
  • strat_pods_experimental

5. Zgodne z Open/Closed Principle

System jest:

  • otwarty na rozszerzenia (dodajesz nowe strategie),
  • zamknięty na modyfikacje (nie dotykasz starego kodu).

🧪 Przykład użycia w Flask

@app.route("/analiza", methods=["POST"])
def analiza():
    wybrana = request.form.get("user_strategy", "strat_pods_v1")

    df = pobierz_dane()
    strategia = get_strategy(wybrana)

    wynik = strategia.analyze(df, prog=10, margines=5)

    return jsonify(wynik.to_dict(orient="records"))

🔥 Podsumowanie

Strategy wystarczy, gdy:

  • masz mało strategii,
  • wybór jest statyczny,
  • nie planujesz rozwoju.

Strategy + Factory jest konieczne, gdy:

  • strategie mają rosnąć,
  • wybór ma być dynamiczny,
  • chcesz wersjonować algorytmy,
  • chcesz architekturę pluginów,
  • chcesz czysty kod bez if‑ów.

📌 Wniosek do zapamiętania

Strategy = różne algorytmy. Factory = wybór algorytmu. Razem = system, który rośnie bez bólu.