Tất nhiên, người ta có thể viện dẫn luật trừu tượng bị rò rỉ , nhưng điều đó không đặc biệt thú vị bởi vì nó cho rằng tất cả các khái niệm trừu tượng đều bị rò rỉ. Người ta có thể tranh luận và chống lại phỏng đoán đó, nhưng nó không có ích gì nếu chúng ta không chia sẻ sự hiểu biết về ý nghĩa của chúng ta bằng cách trừu tượng hóa và những gì chúng ta muốn nói về sự rò rỉ . Do đó, trước tiên tôi sẽ cố gắng phân định cách tôi xem từng điều khoản sau:
Trừu tượng
Định nghĩa trừu tượng yêu thích của tôi bắt nguồn từ APPP của Robert C. Martin :
"Một sự trừu tượng là sự khuếch đại của thiết yếu và loại bỏ những thứ không liên quan."
Do đó, các giao diện không phải là bản thân trừu tượng . Chúng chỉ trừu tượng nếu chúng mang đến những gì quan trọng và che giấu phần còn lại.
Rò rỉ
Cuốn sách Nguyên tắc, mô hình và thực tiễn tiêm phụ thuộc định nghĩa thuật ngữ trừu tượng rò rỉ trong bối cảnh của Dependency Injection (DI). Đa hình và các nguyên tắc RẮN đóng một vai trò lớn trong bối cảnh này.
Từ Nguyên tắc đảo ngược phụ thuộc (DIP), sau đây, một lần nữa trích dẫn APPP, rằng:
"khách hàng [...] sở hữu các giao diện trừu tượng"
Điều này có nghĩa là các máy khách (mã gọi) xác định các trừu tượng mà chúng yêu cầu, sau đó bạn đi và thực hiện trừu tượng hóa đó.
Một sự trừu tượng bị rò rỉ , theo quan điểm của tôi, là một sự trừu tượng vi phạm DIP bằng cách nào đó bao gồm một số chức năng mà khách hàng không cần .
Phụ thuộc đồng bộ
Một khách hàng thực hiện một đoạn logic nghiệp vụ thường sẽ sử dụng DI để tự tách rời khỏi các chi tiết triển khai nhất định, chẳng hạn như, thông thường, cơ sở dữ liệu.
Xem xét một đối tượng miền xử lý yêu cầu đặt chỗ nhà hàng:
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);
}
}
Ở đây, sự IReservationsRepository
phụ thuộc được xác định riêng bởi khách hàng, MaîtreD
lớp:
public interface IReservationsRepository
{
Reservation[] ReadReservations(DateTimeOffset date);
int Create(Reservation reservation);
}
Giao diện này hoàn toàn đồng bộ vì MaîtreD
lớp không cần nó không đồng bộ.
Phụ thuộc không đồng bộ
Bạn có thể dễ dàng thay đổi giao diện thành không đồng bộ:
public interface IReservationsRepository
{
Task<Reservation[]> ReadReservations(DateTimeOffset date);
Task<int> Create(Reservation reservation);
}
Các MaîtreD
lớp, tuy nhiên, không cần các phương pháp đó là không đồng bộ, vì vậy bây giờ các DIP bị vi phạm. Tôi coi đây là một sự trừu tượng rò rỉ, bởi vì một chi tiết thực hiện buộc khách hàng phải thay đổi. Các TryAccept
phương pháp bây giờ cũng đã trở thành không đồng bộ:
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);
}
Không có lý do cố hữu nào để logic miền không đồng bộ, nhưng để hỗ trợ tính không đồng bộ của việc triển khai, điều này hiện bắt buộc.
Lựa chọn tốt hơn
Tại NDC Sydney 2018 tôi đã nói chuyện về chủ đề này . Trong đó, tôi cũng phác thảo một giải pháp thay thế không bị rò rỉ. Tôi cũng sẽ có bài nói chuyện này tại một số hội nghị vào năm 2019, nhưng bây giờ được đổi thương hiệu với tiêu đề mới về tiêm Async .
Tôi dự định cũng sẽ xuất bản một loạt các bài đăng trên blog để đi kèm với bài nói chuyện. Các bài viết này đã được viết và ngồi trong hàng đợi bài viết của tôi, chờ đợi để được xuất bản, vì vậy hãy theo dõi.