我有一个看起来像这样的类结构:
class Parent {
public Parent(int property) { /* use property */}
}
class Son extends Parent {
public Son(int parentProperty, String sonProperty) {
super(parentProperty);
/* use son property */
}
}
我想为这两个类创建构建器,以便:
class ParentBuilder {
protected int parentProperty;
public ParentBuilder parentProperty(int parentPropertyValue) {
parentPropertyValue = parentPropertyValue;
return this;
}
public Parent build() {
return new Parent(parentProperty);
}
}
class SonBuilder extends ParentBuilder {
private String sonProperty;
public SonBuilder sonProperty(String sonProperty) {
this.sonProperty = sonProperty;
return this;
}
@Override
public Son build() {
return new Son(parentProperty, sonProperty);
}
}
但这会导致以下问题:
SonBuilder sonBuilder = new SonBuilder();
sonBuilder.sonProperty("aString").build(); // this works and creates Son
sonBuilder.sonProperty("aString").parentProperty(1).build(); // this works and creates Parent instead of Son
sonBuilder.parentProperty(1).sonProperty("aString").build(); // this doesn't work
我意识到我在挑剔,这可以通过不返回this
来解决(即没有方法链接),但我想知道是否有一个优雅的解决方案。
修改
似乎“优雅”这个词有点混乱。
“优雅”是指一种允许方法链接而不涉及投射的解决方案。
答案 0 :(得分:5)
第一点
sonBuilder.sonProperty("aString").parentProperty(1).build();
这可以创建Parent而不是Son
预计parentProperty()
会返回ParentBuilder
:
public ParentBuilder parentProperty(int parentPropertyValue) {...
ParentBuilder.build()
创建Parent
:
public Parent build() {
return new Parent(parentProperty);
}
第二点
sonBuilder.parentProperty(1).sonProperty("aString").build(); // this doesn't work
如第一点所述,parentProperty()
会返回ParentBuilder
ParentBuilder
当然没有sonProperty()
方法
所以它无法编译。
我想知道是否有一个优雅的解决方案。
优雅的解决方案不是让SonBuilder
继承ParentBuilder
,而是由ParentBuilder
字段组成。
例如:
class SonBuilder {
private String sonProperty;
private ParentBuilder parentBuilder = new ParentBuilder();
public SonBuilder sonProperty(String sonProperty) {
this.sonProperty = sonProperty;
return this;
}
public SonBuilder parentProperty(int parentPropertyValue) {
parentBuilder.parentProperty(parentPropertyValue);
return this;
}
public Son build() {
return new Son(parentBuilder.parentProperty, sonProperty);
}
}
您可以这样创建Son
:
SonBuilder sonBuilder = new SonBuilder();
Son son = sonBuilder.sonProperty("aString").parentProperty(1).build();
答案 1 :(得分:0)
您可以测试build()
方法中存在的父属性。
return (parentProperty == null) ? new Parent(parentProperty, sonProperty) : new Son(sonProperty);
答案 2 :(得分:0)
我不确定它是否可以被视为优雅,但您可以使用强制转换:
template <class T>
struct tag {
tag(T) {}
};
int main() {
tag(int{}); //#1
auto t1 = tag(int{}); //#2
auto t3 = (tag(int{})); //#3
}
答案 3 :(得分:0)
覆盖parentProperty
中的SonBuilder
:
@Override
public SonBuilder parentProperty(int parentPropertyValue) {
super.parentProperty(parentPropertyValue);
return this;
}
这是安全的,因为(只要)super.parentProperty
会返回this
。如果你有不可变的构建器,你的覆盖也会有所不同。
或使用泛型:
abstract class AParentBuilder<T extends Parent> {
protected int parentProperty;
public AParentBuilder<T> parentProperty(int parentPropertyValue) {
parentPropertyValue = parentPropertyValue;
return this;
}
abstract public T build();
}
class ParentBuilder extends AParentBuilder<Parent> {
@Override
public Parent build() {
return new Parent(parentProperty);
}
}
abstract class ASonBuilder<T extends Son> extends AParentBuilder<T> {
private String sonProperty;
public ASonBuilder<T> sonProperty(String sonProperty) {
this.sonProperty = sonProperty;
return this;
}
}
class SonBuilder extends ASonBuilder<Son> {
@Override
public Son build() {
return new Son(parentProperty, sonProperty);
}
}
答案 4 :(得分:0)
另一种方法是在子类方法中使用协变返回类型。
保持SonBuilder
延长ParentBuilder
,但覆盖parentProperty()
以返回SonBuilder
:
@Override
public SonBuilder parentProperty(int parentPropertyValue) {
super.parentProperty(parentPropertyValue);
return this;
}
你仍然可以用这种方式创建儿子:
SonBuilder sonBuilder = new SonBuilder();
Son son = sonBuilder.sonProperty("aString").parentProperty(1).build();
现在parentProperty(1)
调用返回SonBuilder
的子类方法。
这个解决方案可能看起来不错,但它有一个缺点。
它在子类构建器和基础构建器之间创建了强大的耦合
每次在父构建器中添加setProperty方法时,都必须在子类中重写它
否则,您仍然会遇到初始问题:一个不编译的代码,或者它将实例化Parent而不是Son类。
对于完全没有变化的课程,可以接受
否则,应该避免。
使用合成的解决方案更好,因为它允许SonBuilder
按照自己的节奏和自己的规则进化。