もちろん、リークのある抽象化の法則を適用することもできますが、すべての抽象化はリークの多いものであると考えられているため、特に興味深いことではありません。その推測に反対する人もいますが、抽象化とは何か、リークとはどういう意味かを理解していなければ役に立たないのです。したがって、まず、これらの各用語の見方を詳しく説明します。
抽象化
抽象化の私のお気に入りの定義は、Robert C. MartinのAPPPから派生しています。
「抽象化とは、本質的なものを増幅し、無関係なものを排除することです。」
したがって、インターフェイス自体は抽象化ではありません。それらは、重要なことを表面にもたらし、残りを隠す場合にのみ、抽象化です。
漏れやすい
本「依存性注入の原則、パターン、および実践」では、依存性注入(DI)のコンテキストで漏出性抽象化という用語を定義しています。ポリモーフィズムとSOLIDの原則は、このコンテキストで大きな役割を果たします。
依存関係逆転の原則:(DIP)のことが、再びAPPPを引用し、次の次の
"クライアントは[...]抽象インターフェースを所有している"
これが意味することは、クライアント(コードを呼び出す)が必要とする抽象化を定義し、次にその抽象化を実装することです。
漏れやすい抽象化は、私の見解では、何らかの形でクライアントがないことを、いくつかの機能を含むことにより、DIPに違反する抽象化である必要があります。
同期依存関係
ビジネスロジックを実装するクライアントは、通常、DIを使用して、一般にデータベースなどの特定の実装の詳細から切り離します。
レストラン予約のリクエストを処理するドメインオブジェクトについて考えてみましょう。
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);
}
}
ここでは、IReservationsRepository
依存関係はクライアントであるクラスによってのみ決定されますMaîtreD
。
public interface IReservationsRepository
{
Reservation[] ReadReservations(DateTimeOffset date);
int Create(Reservation reservation);
}
このインターフェースは、MaîtreD
クラスが非同期である必要がないため、完全に同期です。
非同期の依存関係
インターフェースを非同期に簡単に変更できます。
public interface IReservationsRepository
{
Task<Reservation[]> ReadReservations(DateTimeOffset date);
Task<int> Create(Reservation reservation);
}
MaîtreD
クラスは、しかし、しない必要があるので、今DIPが侵害され、これらのメソッドは非同期に。実装の詳細によりクライアントが強制的に変更されるため、これは漏洩しやすい抽象化と見なします。TryAccept
この方法は、今も、非同期になることがあります。
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);
}
ドメインロジックが非同期であるという固有の根拠はありませんが、実装の非同期をサポートするために、これが必要になりました。
より良いオプション
NDC Sydney 2018 でこのトピックについて講演しました。その中で、リークしない代替案についても概説します。私もこの講演を2019年のいくつかの会議で行う予定ですが、今は非同期インジェクションという新しいタイトルに変更されています。
講演に伴うブログ記事も公開する予定です。これらの記事はすでに書かれていて、私の記事のキューに入れられており、発行されるのを待っているので、しばらくお待ちください。