异常处理装饰器违反LSP

时间:2019-06-24 07:04:47

标签: dependency-injection decorator liskov-substitution-principle

我目前正在阅读马克·西曼(Mark Seeman)和史蒂文·范·杜尔森(Steven van Deursen)的书“依赖注入”(第二版,但第一版中是相同的例子)。

在第9.2.2节“使用Decorator模式报告异常”中,他提出了一个异常处理装饰器,该装饰器通过捕获某些异常并打开警报框来装饰抽象,而不是将异常冒给消费者。

他说,我遵守SRP和OCP。

但是,我认为这违反了Liskov substitution principle

抽象引发的异常属于合同。如果捕获它们并将它们转换为装饰器中的警报消息,则使用者不会明确知道这一点。消费者要么隐含地知道装饰者已被用来照料异常,在我看来这是奇怪的,或者他不知道,在这种情况下,他不得不照料异常处理以遵守合同。和LSP,这会使装饰器变得毫无用处。

在我看来,消费者一定不知道抽象已装饰过,他应该遵守抽象所提供的合同。

我的建议是使用由适配器实现的新接口IExceptionHandlingAbstraction,该接口可以捕获IAbstraction的异常并将其转换为警报框。这样,消费者可以依靠IExceptionHandlingAbstraction的合同,并且明确知道自己不需要自己处理异常。

因为我非常信任Mark的帖子,答案和书籍,所以我不确定在这里是否缺少任何内容。

1 个答案:

答案 0 :(得分:0)

  

消费者必须不知道抽象已装饰过,他应该遵守抽象所提供的合同。

是正确的。为了遵守LSP,抽象的两种实现都应遵守同一合同。但是现在的问题是:合同到底是什么?消费者可以期待什么?

与Java相比,.NET不会将异常作为方法定义的一部分。我认为这很好。您不能也不应限制抛出抽象的异常。当您抛出期望客户端处理的异常时,这当然会改变。在这种情况下,例外类型应成为合同的一部分。

在该特定装饰器示例IMO中,更大的问题是该异常不会冒出来。它完全被吞下了。当然,这可以视为违反了(隐式)合同,因为开发人员希望在不引发异常的情况下调用成功。因此,我同意该示例有些天真,并且确实违反了LSP。

但是,该示例并不意味着它是完全可靠的100%SOLID解决方案,而是作为示例来演示使用Decorators进行拦截的功能。还要注意的是,在第10章中,我们解释了100%固体不可行也不可取。这都是关于权衡的问题,也许这个装饰器示例在您正在构建的特定客户端应用程序中运行良好,尽管我确实希望使用者(表单)需要知道操作是否成功,因为您通常希望在表单完成后关闭它。但是在那种情况下,我希望第10章中规定的设计是更好的解决方案。

  

我的建议是使用由适配器实现的新接口IExceptionHandlingAbstraction,该适配器捕获IAbstraction的异常并将其转换为警报框

创建一个新的抽象使消费者能够获得有关故障的通知或通知,这是解决LSP违规问题的好方法。

相关问题