Wszyscy używamy na co dzień różnych frameworków i często nawet nie zastanawiamy się jak one funkcjonują. Pomyślałem sobie że napiszę jakąś prostą aplikację bez tej całej magicznej otoczki frameworka i zrobię to w czystej Javie (język w tym przypadku nie ma znaczenia) po czym okazało się że pewne rzeczy są niemożliwe do wykonania.
Weźmy taki przykład. Mamy 2 klasy. Jedna z nich jest Serwisem (albo fasadą - czymś co stanowi pewnego rodzaju API dla naszego modułu/pakietu lub po prostu szeregu funkcjonalności) i udostępnia prostą operację - powiedzmy rejestrację użytkownika. Dodatkowo ten serwis korzysta z innej klasy np. DAO (albo jakieś inne Repository które zapisuje tych użytkowników w bazie/na liście/do pliku) i ta klasa chcielibyśmy aby była prywatna dla modułu/pakietu ponieważ chcemy aby operacje zapisu były wykonywane zawsze przez Serwis a nie z jego pominięciem (bo np. w serwisie może być też dodatkowo walidacja). W jaki sposób zrobić teraz wstrzykiwanie zależności tego DAO do Serwisu np. przez konstruktor bez frameworka? To w ogóle możliwe?
Jeśli jedna z klas jest publiczna a druga podlega pewnym ograniczeniom to nie jesteśmy wstanie utworzyć instancji tej pierwszej w dowolnym miejscu naszej aplikacji. Korzystając z frameworka często mamy taką możliwość. Pytanie więc jak do tego podejść i jak rozwiązać taki problem?
Krótko - potrzebn jest trzecia klasa - taki config modułu gdzie decydujesz co wystawiasz. Fabryką / provider / builder serwisu.
Tworzy Ci domyślną implementację, a jednoczesnie umożliwa wstrzelenie innych zależności w testach:
stary przykład:
https://github.com/javaFunAgain/ratpong/blob/master/src/main/java/pl/setblack/pongi/scores/ScoresModule.java
W tym projekcie nie chowam specjalnie implementacj - ale dzieki tego typu klasom móglbym. (Bardzo zresztą podobne do @Configuration
w Springu )...
Niech Twój prywatny moduł udostępni stworzony obiekt lub lepiej fabrykę czy buildera. A w środku może być użyty DI albo fabryka, jak chcesz.
Korzystając z DI, przynajmniej w świecie .net, jeśli mam widoczność klasy ustawioną na "moduł", to nie użyje jej i nie podepnę w ... nazwijmy to module głównym. Korzystając z frameworków da się to obejść, ale robiąc konfigurację per moduł, a nie ngle uwidaczniać daną klasę w głónym module.
Także tutaj to nie problem (chyba że Java ma inaczej) z użyciem frameworka lub nie, ale z granicami między modułami jakie wyznaczasz.
Potrzebny Ci interfejs dla serwisu, interfejs będzie publiczny dla modułu, a implementacje serwisu i repository/dao prywatne plus dodatkowa jakaś fabryka w której to składanie nastąpi;
public interface IService
{
void Foo();
}
internal class Service : IService
{
private readonly Repository repository;
public Service(Repository repository)
{
this.repository = repository;
}
public void Foo()
{
repository.Foo();
}
}
internal class Repository
{
public void Foo()
{
}
}
public class Factory
{
public IService CreateService()
{
// composition root
return new Service(new Repository());
}
}