Ben koyardım akıcı api buna yaratıyor nesneden kendi "oluşturucu" sınıfı ayrı var etmek. Bu şekilde, müşteri akıcı API'yi kullanmak istemiyorsa yine de manuel olarak kullanabilirsiniz ve etki alanı nesnesini kirletmez (tek bir sorumluluk ilkesine bağlı kalarak). Bu durumda, aşağıdakiler oluşturulacaktır:
Car
etki alanı nesnesi hangisi
CarBuilder
akıcı API tutan
Kullanımı şöyle olurdu:
var car = CarBuilder.BuildCar()
.OfBrand(Brand.Ford)
.OfModel(12345)
.PaintedIn(Color.Silver)
.Build();
CarBuilder
Sınıf (burada C # adlandırma kuralını kullanıyorum) şu şekilde görünecektir:
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;
}
}
Bu sınıfın iş parçacığı güvenli olmayacağını unutmayın (her iş parçacığının kendi CarBuilder örneğine ihtiyacı olacaktır). Ayrıca, akıcı api gerçekten harika bir kavram olsa da, basit etki alanı nesneleri oluşturmak için büyük olasılıkla overkill olduğuna dikkat edin.
Bu anlaşma, çok daha soyut bir şey için bir API oluşturuyorsanız ve daha karmaşık bir kurulum ve yürütme işlemine sahipseniz daha faydalı olur, bu nedenle birim test ve DI çerçevelerinde harika çalışıyor. Kalıcılık, tarih işleme ve alay konusu nesnelerle wikipedia Fluent Interface makalesinin Java bölümünde bazı diğer örnekleri görebilirsiniz .
DÜZENLE:
Yorumlardan da belirtildiği gibi; Builder sınıfını statik bir iç sınıf (Car içinde) yapabilir ve Car değişmez hale getirilebilir. Car'in değişmez kalmasına izin veren bu örnek biraz saçma görünüyor; ancak, daha karmaşık bir sistemde, yerleşik nesnenin içeriğini kesinlikle değiştirmek istemediğiniz durumlarda, bunu yapmak isteyebilirsiniz.
Aşağıda hem statik iç sınıfın nasıl yapılacağına hem de oluşturduğu değişmez bir nesne oluşturmanın nasıl ele alınacağına bir örnek verilmiştir:
// 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();
}
}
Kullanım aşağıdaki gibi olacaktır:
var writer = ImmutableWriter
.GetBuilder()
.Write("peanut butter jelly time")
.Times(2)
.Build();
writer.Perform();
// console writes: peanut butter jelly time peanut butter jelly time
Düzenleme 2: Pete , yorumlarda lambda işlevli inşaatçıların karmaşık etki alanı nesneleriyle yazılan birim testleri bağlamında kullanılması hakkında bir blog yazısı yayınladı . Yapıcıyı biraz daha anlamlı kılmak ilginç bir alternatiftir.
Durumda CarBuilder
bunun yerine bu yöntemi olması gerekir:
public static Car Build(Action<CarBuilder> buildAction = null) {
var carBuilder = new CarBuilder();
if (buildAction != null) buildAction(carBuilder);
return carBuilder._car;
}
Bu da kullanılabilir:
Car c = CarBuilder
.Build(car =>
car.OfBrand(Brand.Ford)
.OfModel(12345)
.PaintedIn(Color.Silver);
var car = new Car(Brand.Ford, 12345, Color.Silver);
?