Endpoint pobiera dane z bazy, wysyła e-mail i od razu tworzy klienta SMTP wewnątrz funkcji. Gdy chcesz przetestować samą logikę bez sieci i prawdziwej bazy, dependency injection zwykle pozwala podmienić te obiekty, choć sama zmiana sposobu ich przekazywania nie naprawi źle podzielonych zadań.

Zależności przekazuj z zewnątrz, a ich składanie zostaw przy wejściu do aplikacji
- Dependency injection oznacza, że obiekt otrzymuje potrzebne mu usługi zamiast tworzyć je samodzielnie.
- Konstruktor jest dobrym miejscem na obowiązkowe zależności, bo od razu widać, czego klasa potrzebuje.
- Ręczne wstrzykiwanie wystarcza w małych programach; kontener DI pomaga, gdy graf obiektów rośnie.
- Test może przekazać atrapę zamiast prawdziwej bazy, klienta API czy usługi wysyłającej wiadomości.
Obiekt przestaje sam wybierać, z czego korzysta
Wyobraź sobie klasę, która zapisuje zamówienie. Jeśli sama tworzy połączenie z bazą, jej kod jest przywiązany do konkretnego sposobu zapisu. Wstrzykiwanie zależności polega na przekazaniu gotowej współpracującej usługi z zewnątrz, na przykład przez argument konstruktora.
W Pythonie nie potrzeba do tego specjalnego słowa kluczowego ani biblioteki. Wystarczą zwykłe obiekty i funkcje. Uważam, że to właśnie prostota ręcznego przekazywania zależności jest najlepszym punktem startu: od razu widać, kto tworzy usługę i kto z niej korzysta.
Konstruktor ujawnia, czego obiekt potrzebuje
Załóżmy, że serwis zamówień ma zapisywać dane i wysyłać potwierdzenie. Zamiast tworzyć repozytorium i nadawcę wiadomości wewnątrz klasy, przekaż je podczas jej budowania:
class OrderService:
def __init__(self, repository, notifier):
self.repository = repository
self.notifier = notifier
def place(self, order):
self.repository.save(order)
self.notifier.send_confirmation(order)
service = OrderService(
repository=SqlOrderRepository(),
notifier=EmailNotifier(),
)
OrderService zna metody swoich współpracowników, ale nie odpowiada za wybór ich konkretnych implementacji. Ten wybór pojawia się przy składaniu aplikacji, czyli w miejscu, które łączy jej elementy.
W teście można przekazać obiekt zapisujący zamówienia do listy i atrapę nadawcy, która tylko rejestruje wywołanie. Jeśli klasa wymaga zależności do działania, umieszczenie ich w konstruktorze jasno pokazuje ten warunek; opcjonalne zachowanie można przekazać inną metodą lub jako jawny argument.
Test rozstrzyga, czy ręczne DI wystarczy
Najczęstszy zysk widać przy testowaniu: zamiast uruchamiać bazę czy kontaktować się z zewnętrznym API, podstawiasz prostą implementację. W poniższym przykładzie test sprawdza zachowanie serwisu, nie konfigurację infrastruktury:
class MemoryOrderRepository:
def __init__(self):
self.orders = []
def save(self, order):
self.orders.append(order)
class FakeNotifier:
def __init__(self):
self.sent = []
def send_confirmation(self, order):
self.sent.append(order)
repo = MemoryOrderRepository()
notifier = FakeNotifier()
service = OrderService(repo, notifier)
service.place({"id": 42})
assert repo.orders == [{"id": 42}]
assert notifier.sent == [{"id": 42}]
Reguła jest prosta: jeśli zależności da się wygodnie utworzyć w jednym miejscu i przekazać konstruktorami, nie dodawaj kontenera. Gdy ręczne składanie powtarza się w wielu punktach albo konfiguracja zależy od środowiska, kontener może uporządkować tworzenie obiektów.
Podobnie działa system zależności w dokumentacji FastAPI: funkcja endpointu deklaruje potrzebną zależność, a framework dostarcza jej wynik. To wygodny mechanizm dla kodu obsługującego żądania, ale w zwykłym skrypcie Python ręczne przekazanie argumentu często jest czytelniejsze.
Kontener pomaga przy dużym grafie, ale nie jest obowiązkowy
Jeśli aplikacja ma wiele usług, repozytoriów i konfiguracji, ręczne tworzenie całego łańcucha może rozlać się po kodzie. Kontener DI zbiera definicje i reguły tworzenia obiektów w jednym miejscu, zamiast wymagać, by każda klasa znała szczegóły całej aplikacji.
Biblioteka Dependency Injector udostępnia między innymi dostawców fabrycznych oraz mechanizm nadpisywania dostawców. Przydaje się to, gdy aplikacja ma kilka konfiguracji lub testy potrzebują innych implementacji. Sama biblioteka nie usuwa jednak zbędnych zależności ani nie naprawia klasy, która robi zbyt wiele.
Uważaj też na pobieranie obiektów z kontenera w dowolnym miejscu programu. Wtedy zależność znika z sygnatury konstruktora i robi się trudniej rozpoznać, czego klasa naprawdę potrzebuje. Preferuj jawne przekazanie zależności; traktuj kontener jako miejsce składania aplikacji, a nie globalny magazyn usług.
Trzy sygnały podpowiadają, którą drogę wybrać
Wybór między ręcznym przekazywaniem, frameworkiem webowym a kontenerem wynika głównie ze skali i miejsca użycia. Poniższa tabela pomaga podjąć decyzję bez wprowadzania dodatkowej abstrakcji na zapas.
| Sytuacja | Co zwykle będzie lepsze | Dlaczego |
|---|---|---|
| Mały skrypt lub kilka klas | Ręczne przekazanie w konstruktorze | Graf obiektów jest czytelny bez dodatkowej konfiguracji. |
| Endpoint w aplikacji FastAPI | Mechanizm zależności frameworka | Framework może dostarczyć wspólne zasoby i logikę powiązaną z żądaniem. |
| Rozbudowana aplikacja z wieloma konfiguracjami | Kontener DI, jeśli ręczne składanie staje się powtarzalne | Definicje tworzenia i podstawiania implementacji trafiają do jednego miejsca. |
W aplikacji webowej z wieloma zasobami granica między „wygodnym” a „nadmiarowym” DI zależy od tego, czy kontener faktycznie porządkuje kompozycję. Jeśli klasa ma długą listę zależności, nie maskuj problemu kontenerem: sprawdź, czy jej odpowiedzialności da się rozdzielić.
Dobra granica zależności zaczyna się od jednej decyzji
Gdy kolejna klasa tworzy własne połączenie, klienta HTTP albo usługę e-mail, sprawdź, czy powinna decydować o ich implementacji. Jeśli nie, przekaż obiekty z zewnątrz; jeśli ich tworzenie pozostaje proste, zostań przy ręcznym składaniu.
Dependency injection to sposób organizacji odpowiedzialności, nie obowiązkowy framework. Zacznij od konstruktora i testowej atrapy, a po kontener sięgnij dopiero wtedy, gdy rozrastająca się konfiguracja zacznie przeszkadzać w zrozumieniu przepływu programu.
