Answers:
如果您是要表示带有例外的单个业务规则检查,那么我认为这不是一个好主意。很多时候,您必须报告一个以上的失败情况,而不是在第一个条件上停止。
另一方面,我确实认为检查所有规则,然后在摘要中引发异常是一种好习惯。
在您给我们的示例中,我认为引发异常是一个坏主意。如果您知道用户在开始工作之前没有得到授权,而您仍然允许他们执行某些功能,然后在他们已经完成任务之后向他们打听消息,那就是一个糟糕的设计。
使用异常来强制执行业务规则不是一个好的设计。
我看不出在创建良好的业务逻辑时抛出异常有什么价值。有数十种处理业务逻辑的方法,这些方法不涉及使用旨在解决系统操作中意外情况的系统。
据预计,在业务逻辑,条件不会得到满足; 这就是首先要使用它的原因,并且您不想背负处理意外I / O故障,内存不足错误和空引用的机制。这些都是系统的故障,但是检测未满足的业务条件是系统的成功运行。
此外,它是一个可以避免意外后果的系统。因为必须在某个时刻捕获异常,所以您冒着使新的业务规则异常陷入不希望的某个地方的风险,或者您可能会在寻找业务规则的代码中捕获实际上并非意味着异常的代码为了它。是的,可以通过良好的编码实践来解决这些情况,但是在任何不平凡的系统中,只要有一位以上的开发人员在工作,就会发生错误,而您只希望它们不会成为代价高昂的错误。
表达业务规则是一回事,执行它们是另一回事
考虑用户体验;如果用户不是销售人员,为什么给他们一个按钮,上面写着“创建发票” 可言?
那完全取决于正在做什么。
首先,您想要的实际行为是什么?如果有人输入自己的信息,则拒绝和对话框(本质上为“您无法做到”)可能是正确的。如果这是一个数据输入人员,使用一堆表格工作,则对话框可能也很好,并且数据输入人员可以将无效的表格放在特殊的堆中。如果要进行批处理,则不希望停顿一切,而要标记它并移至下一个。
一旦有了行为,就需要决定如何实现它。拥有引发异常的业务规则检查器可能是一个好主意。返回返回码并传递给返回码是可能会出错的其他事情,并且您当然不希望错误的输入变得更进一步。
不必担心性能开销。在个人输入数据的情况下,与其他时间相比,这是微不足道的。通常,人类将在该系统中花费最多的时间。对于批处理作业,如果异常是性能问题,那么您输入的不良记录太多了,实际上,处理和重新输入所有这些记录比异常要成问题。
拥有一个健壮且精心设计的一致的异常API是非常合适的。使用它来执行业务规则也可能是适当的。实际上,以我的经验来看,业务规则越复杂,最终以这种方式进行处理的可能性就越大。通常,编写期望异常的系统与编写权威分支逻辑一样容易,甚至要容易得多。
这就是说,通常可以用一个句子描述的简单规则应根据其所在位置以预防性或权威性的方式实施。但是,如果您的规则是多维的,并且需要三个以上或四个以上的因素(特别是如果这些因素的选择是基于一个或多个其他因素),则异常编码可能更易于维护。通常在这些情况下,逻辑路径将有很多先抛出的异常需要抛出(检查无法执行该操作的原因),然后(反之亦然)安全性下降(检查该操作是否被授权) ),有时会需要检查一些权威的累积逻辑(后代/祖先可用性,必须放入对象的先驱状态等)。
这种类型的异常引发的一个好处是,它使您可以在项目的多个区域中分离并重用先例异常。(这是面向方面编程的本质。)通过这样做,您可以将一般业务规则的特定方面封装在一个自包含且可维护的组件中。通常,这些组件将与1-1和抛出的错误消息相对应。(尽管有时您将有一个抛出多个不同异常的组件,但几乎永远不要从多个组件中抛出相同的异常。)
我认为设计基于异常的系统会更加困难,并且由于必须在所有N个级别上构建异常流程,因此初始开发时间会更长。然而,这些系统通常最终变得更加稳定。尽管不可能设计出“不会失败”的系统,但基于异常的设计的好处是您总是可以预见失败。对于大多数人来说,该过程可能违反直觉。这就像在问路,让某人告诉你所有不应该打开的街道。