Jest to właściwie proste, gdy zrozumiesz, że DI dotyczy wzorów i zasad , a nie technologii.
Aby zaprojektować interfejs API w sposób niezależny od kontenera DI, postępuj zgodnie z następującymi ogólnymi zasadami:
Zaprogramuj interfejs, a nie implementację
Ta zasada jest w rzeczywistości cytatem (z pamięci) z Wzorów projektowych , ale zawsze powinna być Twoim prawdziwym celem . DI jest tylko środkiem do osiągnięcia tego celu .
Zastosuj zasadę Hollywood
Zasada Hollywood w kategoriach DI mówi: nie dzwoń do DI Container, zadzwoni do ciebie .
Nigdy nie pytaj bezpośrednio o zależność, wywołując kontener z kodu. Zapytaj o to pośrednio, używając Constructor Injection .
Użyj wtrysku konstruktora
Gdy potrzebujesz zależności, poproś o nią statycznie za pomocą konstruktora:
public class Service : IService
{
private readonly ISomeDependency dep;
public Service(ISomeDependency dep)
{
if (dep == null)
{
throw new ArgumentNullException("dep");
}
this.dep = dep;
}
public ISomeDependency Dependency
{
get { return this.dep; }
}
}
Zwróć uwagę, w jaki sposób klasa usługi gwarantuje niezmienniki. Po utworzeniu instancji zależność jest z pewnością dostępna dzięki kombinacji klauzuli ochronnej i readonly
słowa kluczowego.
Użyj Abstract Factory, jeśli potrzebujesz krótkotrwałego obiektu
Zależności wstrzykiwane za pomocą Constructor Injection bywają długotrwałe, ale czasem potrzebny jest obiekt krótkotrwały lub konstruowanie zależności na podstawie wartości znanej tylko w czasie wykonywania.
Zobacz to, aby uzyskać więcej informacji.
Komponuj tylko w ostatniej odpowiedzialnej chwili
Trzymaj obiekty odłączone do samego końca. Zwykle możesz poczekać i połączyć wszystko w punkcie wejścia aplikacji. Nazywa się to rootem kompozycji .
Więcej informacji tutaj:
Uprość za pomocą fasady
Jeśli uważasz, że wynikowy interfejs API staje się zbyt skomplikowany dla początkujących użytkowników, zawsze możesz podać kilka klas Fasady , które zawierają typowe kombinacje zależności.
Aby zapewnić elastyczną fasadę o wysokim stopniu wykrywalności, możesz rozważyć wprowadzenie Fluent Builders. Coś takiego:
public class MyFacade
{
private IMyDependency dep;
public MyFacade()
{
this.dep = new DefaultDependency();
}
public MyFacade WithDependency(IMyDependency dependency)
{
this.dep = dependency;
return this;
}
public Foo CreateFoo()
{
return new Foo(this.dep);
}
}
Pozwoliłoby to użytkownikowi na utworzenie domyślnego Foo, pisząc
var foo = new MyFacade().CreateFoo();
Byłoby jednak bardzo odkrywalne, że można podać niestandardową zależność i można pisać
var foo = new MyFacade().WithDependency(new CustomDependency()).CreateFoo();
Jeśli wyobrażasz sobie, że klasa MyFacade zawiera wiele różnych zależności, mam nadzieję, że jasne jest, w jaki sposób zapewniłby prawidłowe wartości domyślne, jednocześnie umożliwiając wykrycie rozszerzalności.
FWIW, długo po napisaniu tej odpowiedzi, rozwinąłem zawarte w niej pojęcia i napisałem dłuższy post na blogu o bibliotekach przyjaznych DI oraz post towarzyszący o frameworkach przyjaznych DI .