表示业务规则与例外


16

我知道这很昂贵,但是(IMO)我认为这是一个很好的做法。我说的是这样的规则,例如,如果您不是销售人员,您将无法保存发票。

另一种方法是使对象具有状态或类似状态

还有其他方法吗?你怎么看这件事?

Answers:


15

如果您是要表示带有例外的单个业务规则检查,那么我认为这不是一个好主意。很多时候,您必须报告一个以上的失败情况,而不是在第一个条件上停止。

另一方面,我确实认为检查所有规则,然后在摘要中引发异常是一种好习惯。


1
这正是我在应用程序中处理它的方式。使用FluentValidation使我的生活变得轻松,而ValidateAndThrow方法可以节省我的时间。
Matteo Mosca 2010年

在UI应用程序中,我完全同意,但是在服务层应用程序中,如果传递了不符合业务规则的对象,则会生成异常。如果您将两者结合使用,那么有时您会增加验证功能或matiasha在最后一句话中描述的复杂错误。
条例草案

8
引发特定域的异常。这使您可以将我们非我们分开。

@ThorbjørnRavn Andersen +1“特定于域”的大添加
Justin Ohms

1
@JamesPoulson可能足以将其创建JamesPoulsonException为的子类,RuntimeException然后提供原始异常作为cause。然后,您可以简单地说出 if (exception instanceof JamesPoulsonException)... 区别。使用该getCause()方法可以达到原始异常。

9

在您给我们的示例中,我认为引发异常是一个坏主意。如果您知道用户在开始工作之前没有得到授权,而您仍然允许他们执行某些功能,然后在他们已经完成任务之后向他们打听消息,那就是一个糟糕的设计。

使用异常来强制执行业务规则不是一个好的设计。


我对您所说的了解是,您不同意UI。我可以。我同意您的看法,即友好得多的UI不会让您尝试更新不允许的内容。但这并不意味着不需要在业务核心中进行验证和异常检查。相反,我认为它们是必须的。您需要它们来确保编程不良的UI不允许BL错误使用。
Fede 2010年

8
@Fede:这是关注点分离的问题。UI负责收集用户的意图并报告来自业务层的反馈。业务层的工作是分析UI收集的信息,对其进行分析,然后报告回UI和/或要求数据层保留一些数据。这些层之间应该只有松散的耦合。异常是松耦合的一种糟糕的方法。这不仅意味着在各层之间共享实际的类,而且还调用了旨在处理意外故障的机制。
亚当·克罗斯兰

@Adam-说得好,很明确。
Walter 2010年

1
@Adam-不要关注关注点分离问题。我不希望用户界面中的任何人都可以处理CustomerNameInLowercaseException(请我也希望该异常甚至不存在!)。您可以只处理通用的ValidationException。此外,我100%支持您UI仅收集信息和所有相关信息。我之前说过的是,无论您在UI中做什么,BL都不应该假设每个输入都可以。这只是一些防御性编程。
联邦快递

@Fede:确保每个输入正常,这是业务层的工作。如果UI正在执行,则它正在实现业务逻辑。现在,如果它是一个输入字段被限制为数字的情况下和BL看到字母字符,通过 一切 手段 抛出 一个 异常。每当一个组件从另一个组件接收到无效 输入时,抛出异常是合理且正确的。接口已损坏,系统也已损坏。但是,验证输入与执行业务逻辑非常不同。两件截然不同的事情。
亚当·克罗斯兰

5

我看不出在创建良好的业务逻辑时抛出异常有什么价值。有数十种处理业务逻辑的方法,这些方法不涉及使用旨在解决系统操作中意外情况的系统。

预计,在业务逻辑,条件不会得到满足; 这就是首先要使用它的原因,并且您不想背负处理意外I / O故障,内存不足错误和空引用的机制。这些都是系统的故障,但是检测未满足的业务条件是系统的成功运行。

此外,它是一个可以避免意外后果的系统。因为必须在某个时刻捕获异常,所以您冒着使新的业务规则异常陷入不希望的某个地方的风险,或者您可能会在寻找业务规则的代码中捕获实际上并非意味着异常的代码为了它。是的,可以通过良好的编码实践来解决这些情况,但是在任何不平凡的系统中,只要有一位以上的开发人员在工作,就会发生错误,而您只希望它们不会成为代价高昂的错误。


1
我同意,将异常称为异常是因为不希望它们发生。另一方面,在服务层中强制执行业务规则(带有例外)不一定是错误的。您不希望它被使用,您希望客户端仅调用操作并仅提交符合有效条件的数据。但是您想建立一层保护,因为您知道客户端编码中也会发生错误。有点像在数据库中使用外键约束:您希望人们正确地插入和更新数据,但是知道他们可能由于编程错误而失败。
杰里米(Jeremy)2010年

2

表达业务规则是一回事,执行它们是另一回事

考虑用户体验;如果用户不是销售人员,为什么给他们一个按钮,上面写着“创建发票” 可言


1
仅仅因为您不提供按钮,并不意味着他们无论如何也不会发送消息给您。想轻松安全规则怎么会是如果这是所有花..
jmoreno

如果不允许用户执行X操作,则不要给他们提供“执行X操作”按钮。最好的安全措施是无知-不要告诉用户他们无法做的事情;)
Steven A. Lowe 2012年

1

那完全取决于正在做什么。

首先,您想要的实际行为是什么?如果有人输入自己的信息,则拒绝和对话框(本质上为“您无法做到”)可能是正确的。如果这是一个数据输入人员,使用一堆表格工作,则对话框可能也很好,并且数据输入人员可以将无效的表格放在特殊的堆中。如果要进行批处理,则不希望停顿一切,而要标记它并移至下一个。

一旦有了行为,就需要决定如何实现它。拥有引发异常的业务规则检查器可能是一个好主意。返回返回码并传递给返回码是可能会出错的其他事情,并且您当然不希望错误的输入变得更进一步。

不必担心性能开销。在个人输入数据的情况下,与其他时间相比,这是微不足道的。通常,人类将在该系统中花费最多的时间。对于批处理作业,如果异常是性能问题,那么您输入的不良记录太多了,实际上,处理和重新输入所有这些记录比异常要成问题。


0

拥有一个健壮且精心设计的一致的异常API是非常合适的。使用它来执行业务规则也可能是适当的。实际上,以我的经验来看,业务规则越复杂,最终以这种方式进行处理的可能性就越大。通常,编写期望异常的系统与编写权威分支逻辑一样容易,甚至要容易得多。

这就是说,通常可以用一个句子描述的简单规则应根据其所在位置以预防性或权威性的方式实施。但是,如果您的规则是多维的,并且需要三个以上或四个以上的因素(特别是如果这些因素的选择是基于一个或多个其他因素),则异常编码可能更易于维护。通常在这些情况下,逻辑路径将有很多先抛出的异常需要抛出(检查无法执行该操作的原因),然后(反之亦然)安全性下降(检查该操作是否被授权) ),有时会需要检查一些权威的累积逻辑(后代/祖先可用性,必须放入对象的先驱状态等)。

这种类型的异常引发的一个好处是,它使您可以在项目的多个区域中分离并重用先例异常。(这是面向方面编程的本质。)通过这样做,您可以将一般业务规则的特定方面封装在一个自包含且可维护的组件中。通常,这些组件将与1-1和抛出的错误消息相对应。(尽管有时您将有一个抛出多个不同异常的组件,但几乎永远不要从多个组件中抛出相同的异常。)

我认为设计基于异常的系统会更加困难,并且由于必须在所有N个级别上构建异常流程,因此初始开发时间会更长。然而,这些系统通常最终变得更加稳定。尽管不可能设计出“不会失败”的系统,但基于异常的设计的好处是您总是可以预见失败。对于大多数人来说,该过程可能违反直觉。这就像在问路,让某人告诉你所有不应该打开的街道。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.