类的"set"方法应该返回"void"还是"boolean"?



所以我在设计类时经常遇到这种情况:

class foo {
private Bar bar;
public foo(Bar bar) {
this.bar = bar;
}
public Bar getBar() {
return bar;
}
public void setBar(Bar bar) {
this.bar = bar;
}
}

到目前为止,一切都很好,对吧?但后来我想"我如何知道用户将传递一个可接受的bar对象?那么:

private bool validateBar(Bar bar) {
return amIgood(bar);
}

当然,我需要把它和setBar函数放在一起,就像这样:

public bool setBar(Bar bar) {
if (validateBar(bar)) {
this.bar = bar;
return true;
}
else
return false;
}

好吧,如果这是我需要做的,那么我也必须包括在构造函数中,对吧?除了构造函数没有返回除foo对象之外的任何其他对象的选项之外,所以我尝试并思考解决方法,比如:

public foo(Bar bar) {
if validateBar(bar)
this.bar = bar;
else
throw Exception("Invalid bar passed along to foo");
}

或者:

public foo(Bar bar) {
if (!setBar(bar))
throw Exception("Invalid bar passed along to foo");
}

你可以看到一些简单的事情是如何很快失控的。如果在验证的基础上还要做一些卫生工作,情况会更糟。

所以我的问题是如何在保持类结构相对简单的同时解决验证问题?

编辑setbar的第一个例子本应是void,但意外地放入了bar,现在已更正

以上都不是。

按照惯例,setter返回void,但如果实现fluent接口,则可以返回实例的类型(在本例中为Foo,而不是Bar(,在这种情况下,setter方法的最后一行是return this;。(您的示例缺少返回,因此无法编译(。

如果setter的参数必须是"有效的",则不应影响返回类型。相反,这种方法应该爆炸:

public void setBar(Bar bar) {
if (!validateBar(bar)) {
throw new IllegalArgumentException("Bar is invalid");
}
this.bar = bar;
}

这里不返回boolean的一个很好的理由是,调用者可能不会检查返回,并且可能会盲目地继续,好像一切都很好,这当然会很糟糕。

此外,最好通过在Bar构造函数内移动validateBar()的逻辑,抛出IllegalArgumentException,使其不可能创建无效的Bar。如果传入的参数将创建无效Bar,则可以从Foo中删除检查。

如果validateBar()方法是static,则意味着Bar可以在没有Foo上下文的情况下进行验证,因此应该在Bar中实现该逻辑。如果不需要,Foo不应该负责或知道如何验证Bar

但是,如果Foo对有效的Bar有特殊要求,而该要求在其他地方不适用,则在其构造函数中创建Bar的子类,例如FooBar extends Bar,以实现Foo的特殊验证要求。

如果验证Bar确实需要Foo的上下文来验证,并且不需要在Foos之间重用Bar实例,则Bar类应该是Foo内部类,在这种情况下,验证仍然可以在条形图中。验证逻辑可以根据需要使用constructor, and the state of the containingFoo。

如果验证Bar需要Foo的上下文来验证,则需要重用Bar实例,则Bar不能是内部类,并且验证代码应该位于Foo中,正如您对validateBar(Bar bar)方法所建议的那样,只是它不是static,因此该方法将使用Foo字段。

此外,考虑将其重新命名为validate(Bar bar),因为参数的类型可以清楚地表明正在验证的内容。

最新更新