Problem jest technicznie łatwy dla mnie do zrobienia tak, żeby działało. Ale już nie wiem, jak to rozwiązać tak, by nie było code smellów.
Czytam z dokumentacji Microsoftu:
Microsoft napisał:
- Avoid using the service locator pattern. For example, don't invoke GetService to obtain a service instance when you can use DI instead.
- Another service locator variation to avoid is injecting a factory that resolves dependencies at run time. Both of these practices mix Inversion of Control strategies.
Ma to sens o tyle, o ile w Factory zazwyczaj występuje new
, a o ile pamiętam Microsoft naciskał, by używać ichniego DI zamiast new
(i zamiast static
ofc).
No ale w takim razie czego użyć zamiast Factory?
Przykładowa sytuacja (od której teraz się odbiłem): Zamawianie kuriera. DHL obsługuje sandbox, który tym się różni od nie-sandboxa, że requesty do sandboxa dają odpowiedzi dokładnie takie same, jak do serwisu "normalnego", jednakże nie powodują skutków w rodzaju przyjazdu kuriera pod drzwi o określonej porze. Sandbox jest do testowania.
Niestety DHL nie był łaskaw wyabstrahować tego. Mam klasy DHL.DHL24WebapiClient
oraz SandboxDHL.DHL24WebapiClient
, które udostępniają dokładnie takie same metody, parametry, typy danych - niestety takie same, a nie te same. Klasy te nie implementują żadnego wspólnego interfejsu.
A ja mam w runtime, na podstawie danych wejściowych, a nie w compile time zdecydować, czy chcę wysłać request do sandboxa, czy do "normalnego" API DHLa.
public class DhlWrapper
{
private readonly DHL.DHL24WebapiClient client;
private readonly SandboxDHL.DHL24Webapi clientSandbox;
public DhlWrapper(DHL.DHL24WebapiClient client, SandboxDHL.DHL24Webapi clientSandbox) =>
(this.client, this.clientSandbox) = (client, clientSandbox);
public async Task<BasicShipmentData> CreateShipments(AuthData auth, ShipmentFullData shipmentFullData, bool sandbox=false) =>
sandbox ? await clientSandbox.CreateShipments(auth, shipmentFullData) : await client.CreateShipments(auth, shipmentFullData);
public async Task<DeleteShipmentResult> DeleteShipments(AuthData auth, string[] shipments, bool sandbox=false) =>
sandbox ? await clientSandbox.DeleteShipments(auth, shipments) : await client.DeleteShipments(auth, shipments);
public async Task BookCourier(AuthData auth, /* jeszcze kilka parametrów */, bool sandbox = false) =>
sandbox ? await clientSandbox //.. nie chce mi się dalej pisać, jest jasne co się tu dzieje
// i jeszcze dobre trochę metod
}
Takie rozwiązanie jak to powyżej wydaje mi się być brzydkie?
Ale może właśnie tak, jak powyżej należy pisać? Bo alternatywa, jaką widzę, to factory pattern, która jest niezalecana przez Microsoft.