Wstrzykiwanie zależności w Pythonie bez kontenera DI

Tymoteusz Kowalski • 14 listopada 2026
Sieć połączonych kulek, od niebieskich do pomarańczowych, symbolizuje złożoność systemu, gdzie **dependency injection** ułatwia zarządzanie powiązaniami.

Spis treści

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ń.

Diagram pokazuje, jak DeliveryOrderService używa dependency injection do pobierania danych z Repository, które komunikuje się z Database.

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.

FAQ - Najczęstsze pytania

Obowiązkowe usługi, takie jak repozytorium i nadawca powiadomień, przekaż jako argumenty konstruktora. Dzięki temu wymagania klasy widać od razu, a wybór konkretnych implementacji pozostaje w miejscu składania aplikacji. Opcjonalne zachowanie można przekazać osobną metodą lub jako jawny argument.

Utwórz proste implementacje testowe i przekaż je do serwisu zamiast prawdziwych usług. Przykładowo repozytorium może zapisywać zamówienia na liście, a atrapowy nadawca rejestrować wysłane potwierdzenia. Następnie sprawdź, czy po wywołaniu metody zamówienie znalazło się w obu listach.

Przy małym skrypcie lub kilku klasach zwykle wystarczy utworzyć obiekty w jednym miejscu i przekazać je konstruktorami. Kontener może pomóc, gdy graf obiektów jest rozbudowany, ręczne składanie się powtarza albo konfiguracja zależy od środowiska. Nie naprawi jednak klasy, która ma zbyt wiele odpowiedzialności lub niepotrzebnych zależności.

W FastAPI endpoint deklaruje potrzebną zależność, a framework dostarcza jej wynik, co może ułatwić współdzielenie zasobów i obsługę logiki związanej z żądaniem. W zwykłym skrypcie Python często czytelniej jest samodzielnie przekazać obiekt jako argument. W obu przypadkach zależność powinna być jawna, zamiast pobierana z globalnego magazynu usług.

Oceń artykuł

Ocena: 0.00 Liczba głosów: 0

Tagi

wstrzykiwanie zależności
testy jednostkowe
fastapi
kontenery di
Autor Tymoteusz Kowalski
Tymoteusz Kowalski
Nazywam się Tymoteusz Kowalski i od 7 lat zajmuję się programowaniem, ze szczególnym uwzględnieniem Pythona oraz nowoczesnych technologii. Moja przygoda z programowaniem zaczęła się od fascynacji możliwościami, jakie daje kod, a z czasem przerodziła się w pasję do dzielenia się wiedzą i pomagania innym w zrozumieniu złożonych zagadnień. Interesuje mnie, jak można uprościć trudne tematy, aby były bardziej przystępne dla każdego, niezależnie od poziomu zaawansowania. W moich tekstach staram się dostarczać rzetelne, aktualne i zrozumiałe informacje, a także porównywać różne źródła, aby zapewnić czytelnikom szeroki kontekst. Piszę o praktycznych zastosowaniach Pythona, nowinkach technologicznych oraz najlepszych praktykach programistycznych. Moim celem jest nie tylko przedstawienie teorii, ale także pokazanie, jak można ją zastosować w praktyce, co mam nadzieję, uczyni moją twórczość użyteczną dla każdego, kto pragnie rozwijać swoje umiejętności w programowaniu.

Udostępnij artykuł

Napisz komentarz