Terminologia zawarta w pytaniu tak naprawdę nie pasuje do przykładowego kodu. Ambient Context
Jest wzór używany do przechwycenia zależność od jakiejkolwiek klasy w dowolnym module tak proste, jak to możliwe, bez zanieczyszczających każda klasa przyjąć interfejs zależność, ale zachowując ideę odwrócenie sterowania. Takie zależności są zwykle poświęcone logowaniu, bezpieczeństwu, zarządzaniu sesjami, transakcjom, buforowaniu, audytowi, tak jak w przypadku wszelkich przekrojowych problemów w tej aplikacji. To jakoś irytujące dodaj ILogging
, ISecurity
, ITimeProvider
do konstruktorów i przez większość czasu nie wszystkie zajęcia muszą w tym samym czasie, więc rozumiem Twoje potrzeby.
Co zrobić, jeśli czas życia ISession
instancji jest inny niż ten ILogger
? Może instancja ISession powinna zostać utworzona dla każdego żądania, a ILogger raz. Zatem posiadanie tych wszystkich zależności zarządzanych przez jeden obiekt, który nie jest samym kontenerem, nie wydaje się właściwym wyborem ze względu na te wszystkie problemy z zarządzaniem i lokalizacją przez całe życie i innymi opisanymi w tym wątku.
IAmbientContext
W pytaniu nie rozwiązuje problemu nie zanieczyszczających każdy konstruktor. Nadal musisz go użyć w podpisie konstruktora, jasne, że tym razem tylko raz.
Zatem najłatwiejszym sposobem jest NIE używanie iniekcji konstruktora lub jakiegokolwiek innego mechanizmu iniekcji do radzenia sobie z zależnościami przekrojowymi, ale za pomocą wywołania statycznego . W rzeczywistości często widzimy ten wzorzec, wdrażany przez samą platformę. Sprawdź Thread.CurrentPrincipal, która jest statyczną właściwością zwracającą implementację IPrincipal
interfejsu. Można go również ustawić, więc możesz zmienić implementację, jeśli chcesz, więc nie jesteś z nią związany.
MyCore
wygląda teraz jak
public class MyCoreClass
{
public void BusinessFeature(string data)
{
LoggerContext.Current.Log(data);
_repository.SaveProcessedData();
SessionContext.Current.SetData(data);
...etc
}
}
Ten wzorzec i możliwe implementacje zostały szczegółowo opisane przez Marka Seemanna w tym artykule . Mogą istnieć implementacje oparte na samym kontenerze IoC, z którego korzystasz.
Chcesz uniknąć AmbientContext.Current.Logger
, AmbientContext.Current.Session
z tych samych powodów, jak opisano powyżej.
Ale masz inne opcje rozwiązania tego problemu: użyj dekoratorów, dynamicznego przechwytywania, jeśli twój kontener ma taką możliwość lub AOP. Kontekst otoczenia powinien być ostatecznością, ponieważ klienci kryją w nim swoje zależności. Nadal używałbym Ambient Context, jeśli interfejs naprawdę naśladuje mój impuls do używania zależności statycznej, takiej jak DateTime.Now
lub, ConfigurationManager.AppSettings
a ta potrzeba rośnie dość często. Ale ostatecznie zastrzyk konstruktora może nie być tak złym pomysłem na uzyskanie tych wszechobecnych zależności.
IService
używane do komunikowania się z innymi usługami? JeśliIService
reprezentuje niejasną zależność od innych usług, brzmi to jak lokalizator usług i nie powinien istnieć. Twoja klasa powinna zależeć od interfejsów, które wyraźnie opisują, co zrobi z nimi ich konsument. Żadna klasa nigdy nie potrzebuje usługi, aby zapewnić dostęp do usługi. Klasa potrzebuje zależności, która robi coś konkretnego, czego potrzebuje klasa.