编辑:我想指出,这个问题描述了一个理论上的问题,并且我知道我可以使用构造函数参数作为强制参数,或者如果API使用不正确则抛出运行时异常。然而,我在寻找,它的解决方案不要求构造函数参数或运行时检查。
假设您有一个Car
这样的界面:
public interface Car {
public Engine getEngine(); // required
public Transmission getTransmission(); // required
public Stereo getStereo(); // optional
}
正如评论所暗示的,a Car
必须具有Engine
and,Transmission
而a Stereo
是可选的。这意味着可以build()
将Car
实例创建的Builder 仅应在已将和都指定给该Builder实例的情况下才具有build()
方法。这样,类型检查器将拒绝编译任何尝试在不使用or 的情况下创建实例的代码。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
),即加了一个又一个需要setter方法,包含所有可选setter方法,以及最后一堂课。
这意味着此步骤构建器的用户仅限于作者提供强制设置器的顺序。这是一个可能的用法示例(请注意,它们都是严格排序的: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,该FSM使您可以创建一个Builder
,可以将其转换为BuilderWithEngine
或BuilderWithTransmission
,然后都可以将其转换为CompleteBuilder
,从而实现了build()
方法。可以在任何这些构建器实例上调用可选的setter。
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
?