Seseorang dapat, tentu saja, menerapkan hukum abstraksi bocor , tetapi itu tidak terlalu menarik karena menyatakan bahwa semua abstraksi bocor. Seseorang dapat berdebat untuk dan menentang dugaan itu, tetapi tidak membantu jika kita tidak berbagi pemahaman tentang apa yang kita maksud dengan abstraksi , dan apa yang kita maksudkan dengan bocor . Oleh karena itu, saya akan mencoba menjelaskan bagaimana saya melihat masing-masing istilah ini:
Abstraksi
Definisi favorit saya tentang abstraksi diambil dari Robert C. Martin APPP :
"Sebuah abstraksi adalah penguatan yang esensial dan penghapusan yang tidak relevan."
Dengan demikian, antarmuka bukanlah abstraksi . Mereka hanya abstraksi jika membawa ke permukaan apa yang penting, dan menyembunyikan sisanya.
Bocor
Buku Prinsip, Pola, dan Praktek Injeksi Ketergantungan menentukan istilah abstraksi bocor dalam konteks Injeksi Ketergantungan (DI). Polimorfisme dan prinsip-prinsip SOLID memainkan peran besar dalam konteks ini.
Dari Prinsip Ketergantungan Pembalikan (DIP), berikut ini mengikuti, sekali lagi mengutip APPP, bahwa:
"klien [...] memiliki antarmuka abstrak"
Ini artinya bahwa klien (kode panggilan) mendefinisikan abstraksi yang mereka butuhkan, dan kemudian Anda pergi dan mengimplementasikan abstraksi itu.
Sebuah abstraksi bocor , dalam pandangan saya, adalah sebuah abstraksi yang melanggar DIP oleh entah termasuk beberapa fungsi yang klien tidak perlu .
Ketergantungan sinkron
Klien yang mengimplementasikan sepotong logika bisnis biasanya akan menggunakan DI untuk memisahkan dirinya dari detail implementasi tertentu, seperti, umumnya, database.
Pertimbangkan objek domain yang menangani permintaan reservasi restoran:
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);
}
}
Di sini, IReservationsRepository
ketergantungan ditentukan secara eksklusif oleh klien, MaîtreD
kelas:
public interface IReservationsRepository
{
Reservation[] ReadReservations(DateTimeOffset date);
int Create(Reservation reservation);
}
Antarmuka ini sepenuhnya sinkron karena MaîtreD
kelas tidak perlu asinkron.
Dependensi asinkron
Anda dapat dengan mudah mengubah antarmuka menjadi asinkron:
public interface IReservationsRepository
{
Task<Reservation[]> ReadReservations(DateTimeOffset date);
Task<int> Create(Reservation reservation);
}
The MaîtreD
kelas, bagaimanapun, tidak perlu metode-metode untuk menjadi asynchronous, jadi sekarang DIP ini dilanggar. Saya menganggap ini abstraksi bocor, karena detail implementasi memaksa klien untuk berubah. The TryAccept
Metode saat ini juga sudah menjadi asynchronous:
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);
}
Tidak ada dasar pemikiran untuk logika domain menjadi asinkron, tetapi untuk mendukung asinkron dari implementasi, ini sekarang diperlukan.
Pilihan yang lebih baik
Di NDC Sydney 2018 saya memberi ceramah tentang topik ini . Di dalamnya, saya juga menguraikan alternatif yang tidak bocor. Saya akan memberikan ceramah ini di beberapa konferensi pada tahun 2019 juga, tetapi sekarang diganti dengan judul baru injeksi Async .
Saya berencana untuk juga menerbitkan serangkaian posting blog untuk menemani pembicaraan. Artikel-artikel ini sudah ditulis dan duduk di antrian artikel saya, menunggu untuk diterbitkan, jadi tetap disini.