假设我们有几十个代表我的域的java POJO,也就是我系统中的数据,它作为我系统不同层之间的对象流动。该系统可以是Web应用程序,也可以是简单的桌面应用程序。域名包含的内容并不重要。
在设计我的系统时,我很困惑我应该放置任何验证逻辑。我的POJO(域对象)代表我的数据,这些对象中的一些字段必须符合某些条件,但如果我在我的setter方法中放入了很多验证逻辑,那么告诉调用客户端的唯一方法是抛出异常。如果我不希望系统崩溃,则异常必须是必须捕获和处理的已检查异常。这样做的结果是,每次我使用setter-methods(甚至构造函数)创建一个新对象时,我都会重新抛出该异常或使用try-catch块。在许多setter方法上强制使用try-catch是不对的。
所以问题是我应该把我的验证逻辑放在哪里,这样我就不会在我的代码中混淆大量的样板try-catch块和重新抛出。欢迎最优秀的JAVA字节用户加入讨论。
我已经研究过Google,但没有找到关于这个主题的具体讨论,所以我非常热情地等待,以便更深入地了解事情应该如何完成。
答案 0 :(得分:14)
当你说
时,你可能已经回答了自己的问题这些对象内的一些字段必须符合某些标准
考虑系统中的不变量总是有帮助的,即您想要不惜一切代价维护的东西或必须始终遵循的规则。
您的POJO是最后一道防线"对于数据对象上的这些不变量,因此是适当的 - 甚至是必要的 - 放置验证逻辑的地方。如果没有这样的验证,对象可能不再代表您域中有意义的东西。
这些系统不变量在您的对象(或方法)与其客户"之间形成契约。如果某人试图使用它们(与希望记录良好的合同)相反,抛出异常是正确的做法,因为客户有责任正确使用系统的各个部分。
随着时间的推移,我开始偏向未经检查的异常,而不是任何违反合同的情况,部分是因为您提到的原因,即避免try
- catch
阻止到处。
Java的标准未经检查的例外包括:
最佳做法指南是,当错误被视为可恢复时,已检查例外,否则将未经检查的例外。
Joshua Bloch的第9章" Effective Java, 2nd ed."提供更多关于这个主题的智慧:
上述任何一项都不能阻止您在更高级别使用任何适当的验证逻辑,尤其是强制执行任何特定于上下文的业务规则或约束。
答案 1 :(得分:3)
总而言之,我也不认为有一个独特的解决方案可以满足您的所有需求,它只是取决于您的场景和偏好。
从封装的角度来看,我认为设置器验证是正确的方法,因为它是决定所提供信息是否正确并提供可能错误的详细解释的合理位置。但是我不确定你的意思是:
如果我不希望系统崩溃,则异常必须是已检查的异常......
为什么系统会崩溃?未经检查的异常可以很好地捕获,就像已检查的异常一样。你需要弄清楚你的程序在发生这样的事件时应该如何表现,这样你就可以决定把它们抓到哪里以及做什么。
Checked vs unchecked长期以来仍然以各种方式和信念进行辩论,但我认为没有理由不抛出未经检查的异常。只需制作一个共同的ConfigurationException
(或使用已存在的IllegalArgumentException
)或任何漂浮你的船,相应地标记你的方法签名并添加适当的java文档,以便任何人调用它们知道会发生什么,然后抛出它需要时。
根据您的对象关系和层次结构,另一个解决方案可能是某些builders,它会在创建实例时运行自定义验证。但就像我说它真的取决于场景,你可能无法阻止其他人手动实例化和错误地填充某些对象。
答案 2 :(得分:1)
在某些情况下,解决方案是将所有setter设为私有,并为初始化对象提供单一入口点。然后所有的验证和异常处理都在那个初始化方法中。
这也确保没有对象可以被部分初始化。
对象状态的改变也可以这样处理,通过使对象不可变,除了通过构造函数/初始化方法,如果被滥用,这可能会变得浪费。
答案 3 :(得分:1)
虽然您可以在bean setter方法上放置验证逻辑,但我发现在实践中,更明确的关注点分离是将任何复杂的验证移动到另一个特定于验证的类。听我说。
使用bean验证很好,但在很多情况下(我确定你现在都会遇到),一个简单的注释将无法进行足够的验证。像Spring这样的框架有Validator
个可以实现的功能。
分离验证逻辑有很多好处:
希望有所帮助。