Muszę napisać funkcjonalność postowania ogłoszeń na różnych stronach z ogłoszeniami poprzez ich API w Spring 4. Mianowicie: Ogłoszenie utworzone w moim lokalnym systemie, może być rozsyłane automatycznie na różne zewnętrzne strony. Wiele kodu będzie się powtarzać więc chcę go ujednolicić i spinać wszystkie dane zewnętrznych ofert powiązanych z lokalną do jednej bazy. Zrobię to za pomoca interfejsów, tj. wspólny kod będzie jeden w danych serwisach, a logika mocno zależna od poszczególnych API zewnętrznych będzie wstrzykiwana w formie różnych implementacji serwisów. Jak najlepiej rozróżniać kiedy jaka implementacja ma być wstrzykiwana? Gdzie ta logika powinna byc umieszczona. Myślałem o umieszczeniu tego w konfiguracji beanów i wstrzykiwania zależności przez konstruktory czy to będzie dostatecznie czytalne i czyste rozwiazanie? Chyba jedyne jakie mi wpadło do głowy.

- Rejestracja:ponad 9 lat
- Ostatnio:około miesiąc
- Postów:253

- Rejestracja:ponad 7 lat
- Ostatnio:ponad 6 lat
- Postów:101
Dependency Injection to właśnie nic innego, jak tworzenie obiektów i wrzucanie zależności przez konstruktory. Kontenery DI mogą co najwyżej zautomatyzować pewne czynności. Zatem tak, jest to czytelne i czyste rozwiązanie. Dużo zależy oczywiście od tego, na podstawie czego będziesz podejmował decyzję o tym, której implementacji użyć - Twój post nie mówi za wiele na ten temat, dlatego podam Ci dwa przykłady, które - mam nadzieję - pomogą Ci podjąć właściwą decyzję.
Będę posługiwał się terminem graf obiektów oraz konstruowanie grafu obiektów - mowa tu oczywiście o obiektach klas zarządzanych przez Twój kontener DI i tym, jak są one pospinane. Konstruowanie takiego grafu to moment, kiedy kontener DI rozkminia zależności i decyduje o tym, w jakiej kolejności tworzyć obiekty oraz który gdzie wrzucić.
Przykład #1: masz dwie implementacje interfejsu: A i B. Masz klasę C, która musi dostać jedną z tych dwóch implementacji. Wybór implementacji nie zależy od stanu aplikacji - kontener DI ma wszystkie niezbędne informacje, np. zna jakieś startowe ustawienia konfiguracyjne.
W takim przypadku możesz sobie to w klasie konfiguracyjnej beanów zaimplementować, czyli wybór będzie się dokonywał podczas konstruowania grafu obiektów. Do metody wrzucasz:
- referencje do obu implementacji,
- wartość flagi konfiguracyjnej, na podstawie której wybierzesz jedną z nich.
W metodzie sprawdzasz wartość flagi, tworzysz docelowy obiekt i wrzucasz jedną z tych dwóch implementacji.
Przykład #2: scenariusz dynamiczny, tj. wyboru implementacji możesz dokonać wyłącznie na podstawie bieżącego stanu aplikacji. Np. przetwarzasz jakiś request od użytkownika i musisz wybrać implementację na podstawie danych w nagłówkach opisujących to, co klient akceptuje i rozumie. Wtedy oczywiście implementację możesz wybrać dopiero po odczytaniu i przetworzeniu requestu, czyli robienie tego w klasach konfiguracyjnych lekko odpada.
Tutaj stosuję inne podejście - akurat na co dzień korzystam z innych kontenerów DI (Guice, Dagger) i tam jest coś takiego, jak multibindings, czyli można sobie wstrzyknąć zbiór/mapę wszystkich implementacji:
@Inject
public MojSerwis(Map<String, Something> allSomethings) {
...
}
Zatem tutaj sobie wstrzykuję po prostu taką mapę - wszystkie implementacje mam nazwane i normalnie w ramach serwisu implementuję logikę, która wybierze mi jedną z nich na podstawie danych requestu. Docelowa klasa C raczej dostanie tę implementację nie w konstruktorze, ale podczas wywołania na niej jakiejś metody, która ma coś przeliczyć:
C service = ...
Something chosenImpl = allSomethings.get(getNegotiatedSomething(request));
service.performAction(chosenImpl);
Nie ma tu wielkiej filozofii. Jeśli implementację mogę poznać dopiero tuż przed wywołaniem mojej performAction()
, to nie angażuję do tego kontenera DI.

- Rejestracja:ponad 9 lat
- Ostatnio:około miesiąc
- Postów:253
@zyxist: Super, dziękuję za obszerne wytłumaczenie! moja opcja to dynamiczna opcja 2. User wybiera do jakiego Api chce wysyłać ogłoszenie.
Nie rozumiem tylko jednej rzeczy czemu w przypadku uzycia konfiguracji beanów mówisz "angażuję kontener DI"? W przypadku kiedy nie konfigurujemy beanów, kontener nie jest używany? Przecież jakaś domyślna konfiguracja istnieje, jakoś te dependencje muszą być utworzone i rozwiązane, co w takim razie to robi jesli nie kontener DI?

- Rejestracja:ponad 7 lat
- Ostatnio:ponad 6 lat
- Postów:101
Chodzi o to, że ta część:
C service = ...
Something chosenImpl = allSomethings.get(getNegotiatedSomething(request));
service.performAction(chosenImpl);
To jest już część logiki Twojej aplikacji, a nie logiki kontenera DI. Kontener DI tworzy oczywiście wcześniej potrzebne obiekty, ale wybór implementacji i wrzucenie jej przy wywołaniu metody jako argument realizowane są już gdzie indziej.

- Rejestracja:prawie 8 lat
- Ostatnio:ponad 4 lata
- Lokalizacja:Siemianowice Śląskie
- Postów:500
W springu też można wstrzyknąć zbiór wszystkich implementacji:
@Autowired Processor[] allProcessors;
Potem już w trybie runtime można je przeglądać i wybierać. Można np. do interfejsu dorzucić coś w rodzaju isSupported()
albo getSubjectClass()
. Wtedy kod wybierający implementację przejrzy sobie, która implementacja co potrafi i wybierze odpowiednią. Na przykład procesory, które potrafią przetwarzać wiadomości danego typu mogą ten typ zwracać. Niedawno coś takiego zastosowałem. Mam nadzieję, że nie jest to jakiś antywzorzec :)
Zarejestruj się i dołącz do największej społeczności programistów w Polsce.
Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.