Tôi đã đặt api trôi chảy vào lớp "người xây dựng" riêng biệt với đối tượng mà nó đang tạo. Theo cách đó, nếu khách hàng không muốn sử dụng api trôi chảy, bạn vẫn có thể sử dụng thủ công và nó không gây ô nhiễm đối tượng miền (tuân thủ nguyên tắc trách nhiệm duy nhất). Trong trường hợp này, những điều sau đây sẽ được tạo ra:
Car
đó là đối tượng miền
CarBuilder
chứa API thông thạo
Việc sử dụng sẽ như thế này:
var car = CarBuilder.BuildCar()
.OfBrand(Brand.Ford)
.OfModel(12345)
.PaintedIn(Color.Silver)
.Build();
Các CarBuilder
lớp học sẽ giống như thế này (tôi đang sử dụng C # ước đặt tên ở đây):
public class CarBuilder {
private Car _car;
/// Constructor
public CarBuilder() {
_car = new Car();
SetDefaults();
}
private void SetDefaults() {
this.OfBrand(Brand.Ford);
// you can continue the chaining for
// other default values
}
/// Starts an instance of the car builder to
/// build a new car with default values.
public static CarBuilder BuildCar() {
return new CarBuilder();
}
/// Sets the brand
public CarBuilder OfBrand(Brand brand) {
_car.SetBrand(brand);
return this;
}
// continue with OfModel(...), PaintedIn(...), and so on...
// that returns "this" to allow method chaining
/// Returns the built car
public Car Build() {
return _car;
}
}
Lưu ý rằng lớp này sẽ không an toàn cho luồng (mỗi luồng sẽ cần đối tượng CarBuilder của riêng nó). Cũng lưu ý rằng, mặc dù api trôi chảy là một khái niệm thực sự tuyệt vời, nhưng nó có thể là quá mức cho mục đích tạo ra các đối tượng miền đơn giản.
Thỏa thuận này hữu ích hơn nếu bạn đang tạo API cho một thứ gì đó trừu tượng hơn nhiều và có thiết lập và thực thi phức tạp hơn, đó là lý do tại sao nó hoạt động tuyệt vời trong thử nghiệm đơn vị và khung DI. Bạn có thể xem một số ví dụ khác trong phần Java của bài viết Giao diện thông thạo wikipedia với các đối tượng kiên trì, xử lý ngày và giả.
CHỈNH SỬA:
Theo ghi nhận từ các ý kiến; bạn có thể làm cho lớp Builder trở thành một lớp bên trong tĩnh (bên trong Xe) và Xe có thể được tạo thành bất biến. Ví dụ về việc để cho Car là bất biến có vẻ hơi ngớ ngẩn; nhưng trong một hệ thống phức tạp hơn, nơi bạn hoàn toàn không muốn thay đổi nội dung của đối tượng được xây dựng, bạn có thể muốn làm điều đó.
Dưới đây là một ví dụ về cách thực hiện cả lớp bên trong tĩnh và cách xử lý việc tạo đối tượng bất biến mà nó tạo ra:
// the class that represents the immutable object
public class ImmutableWriter {
// immutable variables
private int _times; private string _write;
// the "complex" constructor
public ImmutableWriter(int times, string write) {
_times = times;
_write = write;
}
public void Perform() {
for (int i = 0; i < _times; i++) Console.Write(_write + " ");
}
// static inner builder of the immutable object
protected static class ImmutableWriterBuilder {
// the variables needed to construct the immutable object
private int _ii = 0; private string _is = String.Empty;
public void Times(int i) { _ii = i; }
public void Write(string s) { _is = s; }
// The stuff is all built here
public ImmutableWriter Build() {
return new ImmutableWriter(_ii, _is);
}
}
// factory method to get the builder
public static ImmutableWriterBuilder GetBuilder() {
return new ImmutableWriterBuilder();
}
}
Cách sử dụng sẽ như sau:
var writer = ImmutableWriter
.GetBuilder()
.Write("peanut butter jelly time")
.Times(2)
.Build();
writer.Perform();
// console writes: peanut butter jelly time peanut butter jelly time
Chỉnh sửa 2: Pete trong các bình luận đã tạo một bài đăng trên blog về việc sử dụng các trình xây dựng với các hàm lambda trong bối cảnh viết các bài kiểm tra đơn vị với các đối tượng miền phức tạp. Đó là một thay thế thú vị để làm cho người xây dựng biểu cảm hơn một chút.
Trong trường hợp CarBuilder
bạn cần phải có phương pháp này thay thế:
public static Car Build(Action<CarBuilder> buildAction = null) {
var carBuilder = new CarBuilder();
if (buildAction != null) buildAction(carBuilder);
return carBuilder._car;
}
Mà có thể được sử dụng như thế này:
Car c = CarBuilder
.Build(car =>
car.OfBrand(Brand.Ford)
.OfModel(12345)
.PaintedIn(Color.Silver);
var car = new Car(Brand.Ford, 12345, Color.Silver);
vậy?