나는 유창한 api 를 자신의 "builder"클래스에 만들고자하는 객체와 분리시켰다. 이렇게하면 클라이언트가 유창한 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 프레임 워크에서 훌륭하게 작동하는 이유입니다. 지속성, 날짜 처리 및 모의 객체와 함께 Wikipedia Fluent Interface 기사의 Java 섹션에서 다른 예제를 볼 수 있습니다 .
편집하다:
의견에서 언급했듯이; Builder 클래스를 정적 내부 클래스 (Car 내부)로 만들고 Car를 변경할 수 없게 만들 수 있습니다. Car를 불변으로 만드는이 예는 약간 어리석은 것 같습니다. 그러나 더 복잡한 시스템에서는 빌드 된 객체의 내용을 절대 변경하지 않으려는 경우 할 수 있습니다.
아래는 정적 내부 클래스를 수행하는 방법과 그것이 생성하는 불변의 객체 생성을 처리하는 방법의 한 예입니다.
// 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);
?