Załóżmy, że mam ten, Service
który odbiera zależności za pośrednictwem konstruktora, ale przed użyciem można go także zainicjować za pomocą niestandardowych danych (kontekstu):
public interface IService
{
void Initialize(Context context);
void DoSomething();
void DoOtherThing();
}
public class Service : IService
{
private readonly object dependency1;
private readonly object dependency2;
private readonly object dependency3;
public Service(
object dependency1,
object dependency2,
object dependency3)
{
this.dependency1 = dependency1 ?? throw new ArgumentNullException(nameof(dependency1));
this.dependency2 = dependency2 ?? throw new ArgumentNullException(nameof(dependency2));
this.dependency3 = dependency3 ?? throw new ArgumentNullException(nameof(dependency3));
}
public void Initialize(Context context)
{
// Initialize state based on context
// Heavy, long running operation
}
public void DoSomething()
{
// ...
}
public void DoOtherThing()
{
// ...
}
}
public class Context
{
public int Value1;
public string Value2;
public string Value3;
}
Teraz - dane kontekstowe nie są wcześniej znane, więc nie mogę zarejestrować ich jako zależności i użyć DI, aby wprowadzić je do usługi
Tak wygląda przykładowy klient:
public class Client
{
private readonly IService service;
public Client(IService service)
{
this.service = service ?? throw new ArgumentNullException(nameof(service));
}
public void OnStartup()
{
service.Initialize(new Context
{
Value1 = 123,
Value2 = "my data",
Value3 = "abcd"
});
}
public void Execute()
{
service.DoSomething();
service.DoOtherThing();
}
}
Jak widać - w grę wchodzą tymczasowe sprzężenia i inicjalizacja zapachów kodu metody, ponieważ najpierw muszę zadzwonić, service.Initialize
aby móc zadzwonić, service.DoSomething
a service.DoOtherThing
potem.
Jakie są inne podejścia, w których mogę wyeliminować te problemy?
Dodatkowe wyjaśnienie zachowania:
Każde wystąpienie klienta musi mieć własne wystąpienie usługi zainicjowane konkretnymi danymi kontekstowymi klienta. Tak więc dane kontekstowe nie są statyczne ani znane z góry, więc nie można ich wprowadzić do konstruktora.
Service
istnieją zależności inne niż teContext
, które nie byłyby zapewnione przezClient
, można je podać za pośrednictwem DI doServiceFactory
przekazania doService
momentucreateService
wywołania.ServiceBuilder partial = new ServiceBuilder().dependency1(dependency1_1).dependency2(dependency2_1).dependency3(dependency3_1);
i pozostać z częściowo skonfigurowaną usługą, a później zrobićService s = partial.context(context).build()
Initialize
Metoda powinna być usunięta zIService
interfejsu użytkownika, jak to jest szczegółowo realizacji. Zamiast tego zdefiniuj inną klasę, która pobiera konkretną instancję usługi i wywołuje na niej metodę inicjalizacji. Następnie ta nowa klasa implementuje interfejs IService:Dzięki temu kod klienta nie będzie wiedział o procedurze inicjalizacji, z wyjątkiem przypadków, w których
ContextDependentService
inicjowana jest klasa. Ograniczasz przynajmniej te części aplikacji, które muszą wiedzieć o tej chwiejnej procedurze inicjalizacji.źródło
Wydaje mi się, że masz tutaj dwie opcje
na przykład.
na przykład.
Wstrzyknięcie fabryki jest w porządku, jeśli chcesz uniknąć przekazywania kontekstu jako parametru. Powiedz, że tylko ta konkretna implementacja potrzebuje kontekstu i nie chcesz dodawać jej do interfejsu
Ale zasadniczo masz ten sam problem, co jeśli fabryka nie ma jeszcze zainicjowanego kontekstu.
źródło
Nie powinieneś zależeć od interfejsu od żadnego kontekstu db i metody inicjalizacji. Możesz to zrobić w konstruktorze klas betonowych.
Odpowiedź na twoje główne pytanie brzmi: Zastrzyk nieruchomości .
W ten sposób możesz wywoływać wszystkie zależności za pomocą Zastrzyku właściwości . Ale może to być ogromna liczba. Jeśli tak, możesz użyć Konstruktora dla nich, ale możesz ustawić swój kontekst według właściwości, sprawdzając, czy jest on pusty.
źródło
Misko Hevery ma bardzo pomocny post na blogu na temat twojej sprawy. Oboje potrzebujesz neowalnego i wstrzykiwalnego dla swojej
Service
klasy, a ten post na blogu może ci pomóc.źródło