Tôi đang tạo một chuỗi các nhà xây dựng để làm sạch cú pháp tạo các lớp miền cho các mocks của tôi như là một phần của việc cải thiện các thử nghiệm đơn vị tổng thể của chúng tôi. Các nhà xây dựng của tôi về cơ bản có một lớp miền (chẳng hạn như một Schedule
) với một số giá trị được xác định bằng cách gọi thích hợp WithXXX
và ghép chúng lại với nhau.Mẫu thiết kế Builder với thừa kế: có cách nào tốt hơn không?
Tôi đã gặp phải một số điểm chung giữa các nhà xây dựng của mình và tôi muốn trừu tượng hóa thành lớp cơ sở để tăng cường sử dụng lại mã. Thật không may những gì tôi kết thúc với hình như:
public abstract class BaseBuilder<T,BLDR> where BLDR : BaseBuilder<T,BLDR>
where T : new()
{
public abstract T Build();
protected int Id { get; private set; }
protected abstract BLDR This { get; }
public BLDR WithId(int id)
{
Id = id;
return This;
}
}
Ghi chú đặc biệt của protected abstract BLDR This { get; }
.
Một thi mẫu của một người thợ xây lớp miền là:
public class ScheduleIntervalBuilder :
BaseBuilder<ScheduleInterval,ScheduleIntervalBuilder>
{
private int _scheduleId;
// ...
// UG! here's the problem:
protected override ScheduleIntervalBuilder This
{
get { return this; }
}
public override ScheduleInterval Build()
{
return new ScheduleInterval
{
Id = base.Id,
ScheduleId = _scheduleId
// ...
};
}
public ScheduleIntervalBuilder WithScheduleId(int scheduleId)
{
_scheduleId = scheduleId;
return this;
}
// ...
}
Vì BLDR không phải là loại BaseBuilder tôi không thể sử dụng return this
trong phương pháp WithId(int)
của BaseBuilder
.
Đang hiển thị loại con với thuộc tính abstract BLDR This { get; }
tùy chọn duy nhất của tôi ở đây hoặc tôi thiếu một số mẹo cú pháp?
Cập nhật (kể từ khi tôi có thể hiển thị do tại sao tôi đang làm điều này rõ ràng hơn một chút):
Kết quả cuối cùng là phải có nhà xây dựng mà xây dựng cấu hình các lớp miền mà người ta mong đợi để lấy từ cơ sở dữ liệu trong một [ lập trình viên] định dạng có thể đọc được. Không có gì sai với ...
mock.Expect(m => m.Select(It.IsAny<int>())).Returns(
new Schedule
{
ScheduleId = 1
// ...
}
);
vì điều này khá dễ đọc. Cú pháp xây dựng thay thế là:
mock.Expect(m => m.Select(It.IsAny<int>())).Returns(
new ScheduleBuilder()
.WithId(1)
// ...
.Build()
);
lợi thế tôi đang tìm ra của việc sử dụng các nhà xây dựng (và thực hiện tất cả các phương pháp WithXXX
) là để tạo tài sản đi phức tạp trừu tượng (tự động mở rộng giá trị tra cứu cơ sở dữ liệu của chúng tôi với đúng Lookup.KnownValues
mà không va vào cơ sở dữ liệu rõ ràng) và có người xây dựng cung cấp hồ sơ kiểm tra thường tái sử dụng cho các lớp miền ...
mock.Expect(m => m.Select(It.IsAny<int>())).Returns(
new ScheduleBuilder()
.AsOneDay()
.Build()
);
Có vẻ như chúng tôi đang mắc kẹt với "tóm tắt T This {get;}" Tôi sẽ chấp nhận và đóng vì tôi nghĩ giải pháp là tất cả những gì có sẵn. – cfeduke
Tôi đang cố gắng tìm một cách tương đối rõ ràng để tạo các nhà xây dựng và tìm thấy bài viết đó (http://www.codeproject.com/Articles/240756/Hierarchically-Implementing-the-Bolchs-Builder-Pat). Nó cung cấp để đúc nó một lần. (Bảo vệ BLDR _this; _this = (BLDR) này; return _this;), nhưng tôi không thể hiểu tại sao tác giả đã thêm lớp WindowBuilder: WindowBuilder {} (Xem ví dụ hoàn chỉnh tại cuối bài viết). Trong bài này tôi thấy hầu như cùng một khuôn mẫu, nhưng cfeduke sử dụng class ScheduleIntervalBuilder: BaseBuilder ... –
Personaly Tôi có lẽ sẽ thực hiện mỗi Builder mà không có lớp cơ sở. Tôi luôn cố gắng không sử dụng mẫu * tự chung này *: http://blogs.msdn.com/b/ericlippert/archive/2011/02/03/curiouser-and-curiouser.aspx – Pellared