On peut, bien sûr, invoquer la loi des abstractions qui fuient , mais ce n'est pas particulièrement intéressant car cela postule que toutes les abstractions sont fuyantes. On peut argumenter pour et contre cette conjecture, mais cela n'aide pas si nous ne partageons pas une compréhension de ce que nous entendons par abstraction et de ce que nous entendons par fuite . Par conséquent, je vais d'abord essayer de définir comment je vois chacun de ces termes:
Abstractions
Ma définition préférée des abstractions est dérivée de l' APPP de Robert C. Martin :
"Une abstraction est l'amplification de l'essentiel et l'élimination de l'inutile."
Ainsi, les interfaces ne sont pas, en soi, des abstractions . Ce ne sont des abstractions que si elles mettent à la surface ce qui compte et cache le reste.
Qui fuit
Le livre Dependency Injection Principles, Patterns and Practices définit le terme d' abstraction qui fuit dans le contexte de l'injection de dépendance (DI). Le polymorphisme et les principes SOLID jouent un grand rôle dans ce contexte.
Du principe d'inversion de dépendance (DIP), il s'ensuit, citant encore APPP, que:
"les clients [...] possèdent les interfaces abstraites"
Cela signifie que les clients (code appelant) définissent les abstractions dont ils ont besoin, puis vous implémentez cette abstraction.
Une abstraction qui fuit , à mon avis, est une abstraction qui viole le DIP en incluant en quelque sorte des fonctionnalités dont le client n'a pas besoin .
Dépendances synchrones
Un client qui implémente un élément de logique métier utilise généralement DI pour se dissocier de certains détails d'implémentation, tels que, généralement, les bases de données.
Considérons un objet de domaine qui gère une demande de réservation de restaurant:
public class MaîtreD : IMaîtreD
{
public MaîtreD(int capacity, IReservationsRepository repository)
{
Capacity = capacity;
Repository = repository;
}
public int Capacity { get; }
public IReservationsRepository Repository { get; }
public int? TryAccept(Reservation reservation)
{
var reservations = Repository.ReadReservations(reservation.Date);
int reservedSeats = reservations.Sum(r => r.Quantity);
if (Capacity < reservedSeats + reservation.Quantity)
return null;
reservation.IsAccepted = true;
return Repository.Create(reservation);
}
}
Ici, la IReservationsRepository
dépendance est déterminée exclusivement par le client, la MaîtreD
classe:
public interface IReservationsRepository
{
Reservation[] ReadReservations(DateTimeOffset date);
int Create(Reservation reservation);
}
Cette interface est entièrement synchrone car la MaîtreD
classe n'a pas besoin qu'elle soit asynchrone.
Dépendances asynchrones
Vous pouvez facilement changer l'interface pour qu'elle soit asynchrone:
public interface IReservationsRepository
{
Task<Reservation[]> ReadReservations(DateTimeOffset date);
Task<int> Create(Reservation reservation);
}
La MaîtreD
classe, cependant, n'a pas besoin de ces méthodes pour être asynchrones, maintenant le DIP est violé. Je considère cela comme une abstraction qui fuit, car un détail d'implémentation oblige le client à changer. La TryAccept
méthode doit désormais également devenir asynchrone:
public async Task<int?> TryAccept(Reservation reservation)
{
var reservations =
await Repository.ReadReservations(reservation.Date);
int reservedSeats = reservations.Sum(r => r.Quantity);
if (Capacity < reservedSeats + reservation.Quantity)
return null;
reservation.IsAccepted = true;
return await Repository.Create(reservation);
}
Il n'y a aucune raison inhérente pour que la logique du domaine soit asynchrone, mais pour prendre en charge l'asynchronie de l'implémentation, cela est maintenant requis.
De meilleures options
Au NDC Sydney 2018, j'ai donné une conférence sur ce sujet . Dans ce document, je présente également une alternative qui ne fuit pas. Je donnerai également cette conférence lors de plusieurs conférences en 2019, mais maintenant renommée avec le nouveau titre d' injection Async .
Je prévois également de publier une série de billets de blog pour accompagner la conférence. Ces articles sont déjà écrits et se trouvent dans ma file d'attente d'articles, en attente de publication, alors restez à l'écoute.