Saya akan menempatkan api yang fasih ke kelas "builder" sendiri terpisah dari objek yang dibuatnya. Dengan begitu, jika klien tidak ingin menggunakan api lancar Anda masih dapat menggunakannya secara manual dan itu tidak mencemari objek domain (mengikuti prinsip tanggung jawab tunggal). Dalam hal ini yang berikut akan dibuat:
Car
yang merupakan objek domain
CarBuilder
yang memegang API lancar
Penggunaannya akan seperti ini:
var car = CarBuilder.BuildCar()
.OfBrand(Brand.Ford)
.OfModel(12345)
.PaintedIn(Color.Silver)
.Build();
The CarBuilder
kelas akan terlihat seperti ini (saya menggunakan C konvensi # penamaan di sini):
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;
}
}
Perhatikan bahwa kelas ini tidak akan aman utas (setiap utas membutuhkan turunan CarBuilder sendiri). Juga perhatikan bahwa, meskipun api fasih adalah konsep yang sangat keren, itu mungkin berlebihan untuk tujuan membuat objek domain sederhana.
Kesepakatan ini lebih berguna jika Anda membuat API untuk sesuatu yang jauh lebih abstrak dan memiliki pengaturan dan pelaksanaan yang lebih kompleks, itulah sebabnya ia bekerja sangat baik dalam pengujian unit dan kerangka kerja DI. Anda dapat melihat beberapa contoh lain di bawah bagian Java artikel wikipedia Fluent Interface dengan ketekunan, penanganan tanggal, dan objek tiruan.
EDIT:
Sebagaimana dicatat dari komentar; Anda bisa membuat kelas Builder kelas dalam statis (di dalam Mobil) dan Mobil bisa dibuat abadi. Contoh membiarkan mobil ini berubah tampaknya agak konyol; tetapi dalam sistem yang lebih kompleks, di mana Anda benar-benar tidak ingin mengubah konten objek yang dibangun, Anda mungkin ingin melakukannya.
Di bawah ini adalah salah satu contoh bagaimana melakukan kelas batin statis dan bagaimana menangani penciptaan objek abadi yang dibangun:
// 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();
}
}
Penggunaannya adalah sebagai berikut:
var writer = ImmutableWriter
.GetBuilder()
.Write("peanut butter jelly time")
.Times(2)
.Build();
writer.Perform();
// console writes: peanut butter jelly time peanut butter jelly time
Sunting 2: Pete dalam komentar membuat posting blog tentang menggunakan pembangun dengan fungsi lambda dalam konteks penulisan unit test dengan objek domain yang kompleks. Ini adalah alternatif yang menarik untuk membuat pembangun sedikit lebih ekspresif.
Jika CarBuilder
Anda perlu memiliki metode ini sebagai gantinya:
public static Car Build(Action<CarBuilder> buildAction = null) {
var carBuilder = new CarBuilder();
if (buildAction != null) buildAction(carBuilder);
return carBuilder._car;
}
Yang bisa digunakan seperti ini:
Car c = CarBuilder
.Build(car =>
car.OfBrand(Brand.Ford)
.OfModel(12345)
.PaintedIn(Color.Silver);
var car = new Car(Brand.Ford, 12345, Color.Silver);
?