Java POJO是否应该在setter方法中进行字段验证并抛出异常?
Java POJO是否应该在setter方法中进行字段验证并抛出异常?
假设我们有数十个Java的POJO,它们代表了我的领域,也就是系统中在各个层之间以对象形式流动的数据。这个系统可以是一个Web应用程序,也可以是一个简单的桌面应用程序。领域的内容并不重要。\n在设计我的系统时,我困惑于应该在哪里放置验证逻辑。我的POJO(领域对象)代表我的数据,其中一些字段必须符合某些标准,但如果我在setter方法中放置了大量的验证逻辑,那么告诉调用客户端的唯一方式就是抛出异常。如果我不想让系统崩溃,异常必须是一个需要捕获和处理的已检查异常。这样做的后果是,每当我使用setter方法(甚至构造函数)创建一个新对象时,我必须要么重新抛出异常,要么使用try-catch块。强制在许多setter方法上使用try-catch并不合适。\n所以问题是我应该将我的验证逻辑放在哪里,以便不在我的代码中塞满大量的样板try-catch块和重新抛出异常。最好的JAVA字节吃货们欢迎加入讨论。\n我已经进行了研究和搜索,但没有找到关于这个主题的具体讨论,所以我非常期待能够深入了解应该如何做事情。
Java POJO(Plain Old Java Object)是一种简单的Java对象,通常只包含私有字段和公共的getter和setter方法。然而,有人提出了一个问题:Java POJO是否应该在setter方法中进行字段验证并抛出异常?下面我们来探讨一下这个问题的原因和解决方法。
问题的出现原因是在某些情况下,我们希望在设置字段值时进行验证,以确保数据的有效性。这样可以避免在后续使用对象时出现错误的数据。另外,通过在setter方法中抛出异常,我们可以更早地发现和处理错误,提高代码的健壮性。
然而,有人提出了一种解决方法:将所有的setter方法设为私有,并提供一个初始化方法作为唯一的入口点。在这个初始化方法中进行验证和异常处理。这样做的好处是确保对象的完整初始化,避免对象处于部分初始化的状态。
另外,这种解决方法还可以通过使对象在构造函数/初始化方法之外不可变,来处理对象状态的改变。虽然这种方法可以防止对象被滥用,但过度使用可能会导致资源的浪费。
Java POJO是否应该在setter方法中进行字段验证并抛出异常是一个有争议的问题。通过将所有的setter方法设为私有,并提供一个初始化方法作为唯一的入口点,可以确保对象的完整初始化,并集中处理验证和异常。此外,还可以通过使对象在构造函数/初始化方法之外不可变来处理对象状态的改变。然而,这种做法是否适用取决于具体的应用场景和需求。
问题的出现的原因以及解决方法:
问题的出现的原因:
1. 从封装的角度来看,setter方法是确定提供的信息是否正确并提供详细解释的逻辑位置。
2. 对于unchecked exception和checked exception的选择存在争议,但没有理由不抛出unchecked exception。
3. 对象关系和层次结构可能影响解决方法的选择。
解决方法:
1. 在setter方法中进行字段验证并抛出异常。
2. 可以选择抛出unchecked exception,如ConfigurationException或IllegalArgumentException。
3. 使用Builder pattern,在创建实例时运行自定义验证。
4. 对于无法阻止其他人手动实例化和错误填充某些对象的情况,无法完全解决该问题。
以下是整理后的文章:
应该在Java POJO中具有字段验证并在setter方法中抛出异常吗?
从封装的角度来看,我认为在setter方法中进行验证是正确的做法,因为这是确定提供的信息是否正确并提供详细解释的逻辑位置。然而,有人可能会问:“如果我不想让系统崩溃,异常必须是checked exception吗?”为什么会导致系统崩溃呢?unchecked exception和checked exception都可以被很好地捕获,您需要确定程序在发生此类事件时应该如何行为,以便决定在何处捕获它们并采取相应的措施。
关于checked exception和unchecked exception的选择一直存在争议,但我认为没有理由不抛出unchecked exception。您可以创建一个通用的ConfigurationException(或使用已有的IllegalArgumentException),相应地标记方法签名并添加适当的Java-docs,以便调用它们的人知道可以期望什么,并在需要时抛出异常。
根据对象关系和层次结构,另一种解决方法可能是使用构建器模式(Builder pattern),在创建实例时运行自定义验证。但正如我之前提到的,这确实取决于具体情况,您可能无法阻止其他人手动实例化并错误填充某些对象。
总之,我也认为没有一个适用于所有需求的唯一解决方案,最终取决于您的场景和偏好。解决此问题的方法可能因具体情况而异。
Java POJO是否应该在setter方法中进行字段验证并抛出异常?
在系统中,你可能已经回答了自己的问题,当你说到一些字段必须符合特定的条件时。思考系统中的不变量总是有帮助的,即你希望始终保持或必须始终遵循的规则。你的POJO是维护数据对象不变量的“最后防线”,因此是放置验证逻辑的适当位置,甚至是必要的位置。如果没有这样的验证,对象可能不再代表在领域中有意义的东西。
这些系统不变量形成对象(或方法)与其“客户端”之间的契约。如果有人试图违反(希望有良好文档记录的)契约来使用它们,抛出异常是正确的做法,因为客户端有责任正确使用系统的各个部分。
随着时间的推移,我开始更喜欢无需检查的异常而不是有检查的异常,其中一个原因是你提到的原因,即避免在各个地方使用try-catch块。Java的标准无需检查的异常包括:
- NullPointerException
- IllegalArgumentException
- IllegalStateException
- UnsupportedOperationException
使用有检查的异常的最佳实践指南是,当错误被认为是可恢复的时使用,否则使用无需检查的异常。Joshua Bloch的《Effective Java, 2nd ed.》第9章对此主题提供了更多的智慧:
- 条目57:仅在异常情况下使用异常
- 条目58:对于可恢复的情况使用有检查的异常,对于编程错误使用运行时异常
- 条目59:避免不必要地使用有检查的异常
- 条目60:更倾向于使用标准异常
上述内容都不应妨碍你在更高层级使用任何适当的验证逻辑,特别是用于强制执行任何特定上下文的业务规则或约束。
是的,POJO必须始终处于有效状态。我确实研究了Effective Java中的这些条目,但它们很难理解,我认为对于懂得面向对象设计原则但无法正确决策如何应用这些条目的中级开发人员来说,它们肯定会显得困难。例如,我不明白他具体指的“可恢复”和“编程错误”。一些具体且可能不同的示例可以帮助理解这些建议背后的强大思想。他所说的“不必要”,我们是否有任何众所周知的例子。
我同意Bloch的书需要仔细阅读,你提出的问题本身都会是很好的Stack Overflow问题。编程错误是违反合同的事情,例如在记录中不支持传递null或负索引。可恢复的错误的例子可能是超时,在这种情况下,调用者可以决定稍后重试或放弃;使用有检查的异常强制调用者处理这个错误并做出决策。使用有检查的异常来进行流程控制而不是使用IF-ELSE块是一个不必要使用的例子。