편집 : 이 질문은 이론적 문제를 설명하고 싶습니다. 필수 매개 변수에 생성자 인수를 사용하거나 API가 잘못 사용되면 런타임 예외가 발생할 수 있음을 알고 있습니다. 그러나 생성자 인수 또는 런타임 검사가 필요 없는 솔루션을 찾고 있습니다.
다음 Car
과 같은 인터페이스 가 있다고 상상해보십시오 .
public interface Car {
public Engine getEngine(); // required
public Transmission getTransmission(); // required
public Stereo getStereo(); // optional
}
의견이 제안 같이,이 Car
이 있어야 Engine
하고 Transmission
있지만은 Stereo
선택 사항입니다. 즉 build()
, Car
인스턴스를 가질 수있는 빌더 는 build()
메소드 Engine
와 Transmission
둘 다 이미 빌더 인스턴스에 제공된 경우 에만 메소드 를 가져야합니다. 이렇게하면 형식 검사기가 또는 Car
없이 인스턴스 를 만들려고 시도하는 코드를 컴파일하지 않습니다 .Engine
Transmission
이것은 단계 빌더를 요구 합니다. 일반적으로 다음과 같은 것을 구현합니다.
public interface Car {
public Engine getEngine(); // required
public Transmission getTransmission(); // required
public Stereo getStereo(); // optional
public class Builder {
public BuilderWithEngine engine(Engine engine) {
return new BuilderWithEngine(engine);
}
}
public class BuilderWithEngine {
private Engine engine;
private BuilderWithEngine(Engine engine) {
this.engine = engine;
}
public BuilderWithEngine engine(Engine engine) {
this.engine = engine;
return this;
}
public CompleteBuilder transmission(Transmission transmission) {
return new CompleteBuilder(engine, transmission);
}
}
public class CompleteBuilder {
private Engine engine;
private Transmission transmission;
private Stereo stereo = null;
private CompleteBuilder(Engine engine, Transmission transmission) {
this.engine = engine;
this.transmission = transmission;
}
public CompleteBuilder engine(Engine engine) {
this.engine = engine;
return this;
}
public CompleteBuilder transmission(Transmission transmission) {
this.transmission = transmission;
return this;
}
public CompleteBuilder stereo(Stereo stereo) {
this.stereo = stereo;
return this;
}
public Car build() {
return new Car() {
@Override
public Engine getEngine() {
return engine;
}
@Override
public Transmission getTransmission() {
return transmission;
}
@Override
public Stereo getStereo() {
return stereo;
}
};
}
}
}
다른 빌더 클래스 ( Builder
,, ) 체인이 있으며 BuilderWithEngine
, CompleteBuilder
필요한 세터 메소드를 하나씩 추가하고 마지막 클래스에는 모든 선택적 세터 메소드가 포함됩니다.
이것은이 단계 작성기의 사용자가 작성자가 필수 세터를 사용할 수있는 순서로 제한되어 있음을 의미합니다 . 다음은 가능한 사용법의 예입니다 ( engine(e)
처음에는 순서대로 , 그 뒤에는 transmission(t)
선택 사항 임 stereo(s)
).
new Builder().engine(e).transmission(t).build();
new Builder().engine(e).transmission(t).stereo(s).build();
new Builder().engine(e).engine(e).transmission(t).stereo(s).build();
new Builder().engine(e).transmission(t).engine(e).stereo(s).build();
new Builder().engine(e).transmission(t).stereo(s).engine(e).build();
new Builder().engine(e).transmission(t).transmission(t).stereo(s).build();
new Builder().engine(e).transmission(t).stereo(s).transmission(t).build();
new Builder().engine(e).transmission(t).stereo(s).stereo(s).build();
그러나 특히 빌더에 세터뿐만 아니라 가산기가 있거나 사용자가 빌더의 특정 특성을 사용할 수있는 순서를 제어 할 수없는 경우, 이는 빌더 사용자에게 이상적이지 않은 시나리오가 많이 있습니다.
내가 생각할 수있는 유일한 해결책은 매우 복잡합니다. 필수 속성의 모든 조합이 설정되었거나 아직 설정되지 않은 경우, 나는 다른 필수 강제 세터를 호출하기 전에 호출 해야하는 것을 알고있는 전용 빌더 클래스를 만들었습니다. build()
메소드가 사용 가능해야하는 상태 및 해당 세터 각각이 build()
메소드 를 포함하는 한 단계에 가까운 더 완전한 유형의 빌더를 리턴합니다 .
나는 아래의 코드를 추가,하지만 당신은 내가 당신이를 만들 수있는 FSM 만들 유형의 시스템을 사용하고 있다고 말할 수 Builder
중 하나로 전환 할 수있다, BuilderWithEngine
또는 BuilderWithTransmission
둘 다 다음으로 전환 될 수있는 CompleteBuilder
, 어떤 구현build()
방법. 이러한 빌더 인스턴스에서 선택적 세터를 호출 할 수 있습니다.
public interface Car {
public Engine getEngine(); // required
public Transmission getTransmission(); // required
public Stereo getStereo(); // optional
public class Builder extends OptionalBuilder {
public BuilderWithEngine engine(Engine engine) {
return new BuilderWithEngine(engine, stereo);
}
public BuilderWithTransmission transmission(Transmission transmission) {
return new BuilderWithTransmission(transmission, stereo);
}
@Override
public Builder stereo(Stereo stereo) {
super.stereo(stereo);
return this;
}
}
public class OptionalBuilder {
protected Stereo stereo = null;
private OptionalBuilder() {}
public OptionalBuilder stereo(Stereo stereo) {
this.stereo = stereo;
return this;
}
}
public class BuilderWithEngine extends OptionalBuilder {
private Engine engine;
private BuilderWithEngine(Engine engine, Stereo stereo) {
this.engine = engine;
this.stereo = stereo;
}
public CompleteBuilder transmission(Transmission transmission) {
return new CompleteBuilder(engine, transmission, stereo);
}
public BuilderWithEngine engine(Engine engine) {
this.engine = engine;
return this;
}
@Override
public BuilderWithEngine stereo(Stereo stereo) {
super.stereo(stereo);
return this;
}
}
public class BuilderWithTransmission extends OptionalBuilder {
private Transmission transmission;
private BuilderWithTransmission(Transmission transmission, Stereo stereo) {
this.transmission = transmission;
this.stereo = stereo;
}
public CompleteBuilder engine(Engine engine) {
return new CompleteBuilder(engine, transmission, stereo);
}
public BuilderWithTransmission transmission(Transmission transmission) {
this.transmission = transmission;
return this;
}
@Override
public BuilderWithTransmission stereo(Stereo stereo) {
super.stereo(stereo);
return this;
}
}
public class CompleteBuilder extends OptionalBuilder {
private Engine engine;
private Transmission transmission;
private CompleteBuilder(Engine engine, Transmission transmission, Stereo stereo) {
this.engine = engine;
this.transmission = transmission;
this.stereo = stereo;
}
public CompleteBuilder engine(Engine engine) {
this.engine = engine;
return this;
}
public CompleteBuilder transmission(Transmission transmission) {
this.transmission = transmission;
return this;
}
@Override
public CompleteBuilder stereo(Stereo stereo) {
super.stereo(stereo);
return this;
}
public Car build() {
return new Car() {
@Override
public Engine getEngine() {
return engine;
}
@Override
public Transmission getTransmission() {
return transmission;
}
@Override
public Stereo getStereo() {
return stereo;
}
};
}
}
}
당신이 알 수 있듯이,이 것이 필요한 다른 빌더 클래스의 수로서, 확장 성이 좋지 않습니다 O (2 ^ n은) 여기서 n은 필수 세터의 수입니다.
따라서 내 질문 : 이보다 우아하게 수행 할 수 있습니까?
(Scala도 수용 가능하지만 Java와 작동하는 답변을 찾고 있습니다)
.engine(e)
한 빌더에 대해 두 번 호출한다는 것은 무엇을 의미 합니까?
build()
당신이 호출되지 않은 경우 engine(e)
및 transmission(t)
전.
Engine
구현 으로 시작 하고 나중에보다 구체적인 구현으로 덮어 쓸 수 있습니다. 그러나 engine(e)
세터가 아니라 가산기 라면 이것이 더 의미가 addEngine(e)
있습니다. 이는 Car
하나 이상의 엔진 / 모터로 하이브리드 자동차를 생산할 수 있는 제작자에게 유용합니다 . 이것은 좋은 예이므로, 왜 당신이 이것을 원하는지에 대한 세부 사항은 간결하게 설명하지 않았습니다.
this
없습니까?