所以我在设计类时经常遇到这种情况:
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
的上下文来验证,并且不需要在Foo
s之间重用Bar
实例,则Bar
类应该是Foo
的内部类,在这种情况下,验证仍然可以在条形图中。验证逻辑可以根据需要使用constructor, and the state of the containing
Foo。
如果验证Bar
需要Foo
的上下文来验证,则需要和重用Bar
实例,则Bar
不能是内部类,并且验证代码应该位于Foo
中,正如您对validateBar(Bar bar)
方法所建议的那样,只是它不是static
,因此该方法将使用Foo
字段。
此外,考虑将其重新命名为validate(Bar bar)
,因为参数的类型可以清楚地表明正在验证的内容。