【问题标题】:Should the "set" method(s) of a class return "void" or "boolean"?类的“set”方法应该返回“void”还是“boolean”?
【发布时间】:2021-03-11 21:06:20
【问题描述】:

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

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,现已更正

【问题讨论】:

  • "类的“set”方法应该返回“void”还是“boolean”?” - 它们应该返回this</opinion>如果参数不合法,为什么不抛出IllegalArgumentException呢?如果设置器有一些逻辑,即必须进行一些验证,那么它不应该是设置器,而是validateAndSet...(...)(或等效)。
  • @Turing85 我非常不同意你的validateAndSet...(...) 想法。如果您无法向 setter 添加验证,那么它们的意义何在?
  • @BrianMcCutchon 依赖于验证的“重量”。如果它很简单(例如空检查),那么设置器是合理的。如果它更复杂,那么我建议不要将它放在 setter 中。
  • @BrianMcCutchon 我同意一般编程,但通常你只需要提供你使用的框架的设置器,如 Jackson、mapstruct、JPA 等。

标签: java class validation oop constructor


【解决方案1】:

以上都不是。

通常,setter 返回void,但如果实现fluent interface,则可能返回实例的类型(在这种情况下为Foo,而不是Bar,在这种情况下,setter 方法的最后一行是@987654325 @。 (您的示例缺少返回,因此无法编译)。

如果设置器的参数必须是“有效的”,那不应该影响返回类型。相反,该方法应该爆炸:

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

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

此外,最好通过将validateBar() 的逻辑移动到Bar 构造函数中来避免创建无效的Bar,如果传入的参数会创建无效的@987654331,则抛出IllegalArgumentException @,然后你可以从Foo中删除检查。

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

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

如果验证Bar确实需要Foo的上下文来验证,并且在Foos之间重用Bar实例是不需要的,那么Bar 类应该是Fooinner 类,在这种情况下,验证仍然可以在 Barconstructor, and the state of the containingFoo` 中根据需要对验证逻辑可用。 p>

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

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

【讨论】:

  • 谢谢,这个答案对我来说很有意义。类在启动类之前使用静态验证功能会是一种好的形式吗?此外,没有退货是一个错误,它应该是无效的,现在更正了。
  • @spiff 我在答案中添加了更多内容。如果这不能回答您的问题,请告诉我。
  • 即“链式”方法。如果这个答案不在这里,我会写自己+1
猜你喜欢
  • 2016-02-12
  • 2010-10-15
  • 1970-01-01
  • 2014-09-26
  • 2012-03-12
  • 1970-01-01
  • 2019-06-29
  • 2016-07-09
  • 2016-03-17
相关资源
最近更新 更多