私は置くところ流暢なAPIを、それが作成されたオブジェクトから自分の「ビルダー」クラス別々だし。そうすれば、クライアントがFluent APIを使用したくない場合でも手動で使用でき、ドメインオブジェクトを汚染することはありません(単一の責任原則に従って)。この場合、次のものが作成されます。
Car
これはドメインオブジェクトです
CarBuilder
流れるようなAPIを保持する
使用法は次のようになります。
var car = CarBuilder.BuildCar()
.OfBrand(Brand.Ford)
.OfModel(12345)
.PaintedIn(Color.Silver)
.Build();
CarBuilder
このクラスは、(私はここではC#の命名規則を使用しています)、次のようになります。
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;
}
}
このクラスはスレッドセーフではないことに注意してください(各スレッドには独自のCarBuilderインスタンスが必要です)。また、流れるようなAPIは本当にクールな概念ですが、単純なドメインオブジェクトを作成するためには多すぎると思われます。
この取り引きは、より抽象的なもの用のAPIを作成し、より複雑なセットアップと実行を行う場合に便利です。そのため、単体テストとDIフレームワークでうまく機能します。ウィキペディアのFluent Interface記事のJavaセクションで、永続性、日付処理、およびモックオブジェクトを使用した他の例を見ることができます。
編集:
コメントからわかるように。Builderクラスを静的な内部クラス(Car内)にし、Carを不変にすることができます。Carを不変にするこの例は少しばかげているように見えます。しかし、構築されたオブジェクトの内容を絶対に変更したくない、より複雑なシステムでは、変更したい場合があります。
以下は、静的内部クラスを実行する方法と、それが構築する不変オブジェクトの作成を処理する方法の1つの例です。
// 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();
}
}
使用法は次のとおりです。
var writer = ImmutableWriter
.GetBuilder()
.Write("peanut butter jelly time")
.Times(2)
.Build();
writer.Perform();
// console writes: peanut butter jelly time peanut butter jelly time
編集2:コメントのPeteは、複雑なドメインオブジェクトを使用した単体テストの作成のコンテキストでラムダ関数を使用したビルダーの使用に関するブログ投稿を行いました。ビルダーをもう少し表現力豊かにする興味深い代替手段です。
CarBuilder
代わりにこのメソッドが必要な場合:
public static Car Build(Action<CarBuilder> buildAction = null) {
var carBuilder = new CarBuilder();
if (buildAction != null) buildAction(carBuilder);
return carBuilder._car;
}
これとして使用できます:
Car c = CarBuilder
.Build(car =>
car.OfBrand(Brand.Ford)
.OfModel(12345)
.PaintedIn(Color.Silver);
var car = new Car(Brand.Ford, 12345, Color.Silver);
ですか?