检查异常是不是违反了开放封闭原则?

Posted

技术标签:

【中文标题】检查异常是不是违反了开放封闭原则?【英文标题】:Do checked exceptions violate the open closed principle?检查异常是否违反了开放封闭原则? 【发布时间】:2019-02-26 09:30:19 【问题描述】:

我有两个检查异常:TestException1TestException2 以及以下代码:

void p1() throws TestException1
    p2();


void p2() throws TestException1  
    p3();


void p3() throws TestException1 

如下修改p3的签名是否违反开闭原则?

void p3() throws TestException1, TestException2 

【问题讨论】:

有什么理由会这样吗? 因为 p3() 对扩展是关闭的。 p3 的任何扩展(在这种情况下添加新的已检查异常)都意味着编辑 p2() 和 p1() 签名。 哦,我没看到p2叫p3。我不会将向现有方法添加异常称为违反 Open-Closed 原则,因为它不会像版本兼容性那样影响软件设计,这更多是架构问题。 【参考方案1】:

我想我现在明白你的问题是什么意思了。 (第二次尝试)

严格来说,您对类的源代码所做的任何更改都违反了开放封闭原则的“封闭”部分。违规的严重程度取决于更改的性质。

在您的示例中,更改 Java 中公共 API 方法抛出的已检查异常是一种严重违规行为。如果您不重新编译,则可能导致使用方法的任何方法中的编译错误 ... 或由二进制兼容性问题引起的Error 子类异常。事实上,由于p3p2 调用,并且由p1 间接调用,因此您实际上需要更改更多类以使其编译。这可能会使 API 更改的范围更大。

所以你的问题:

检查异常是否违反了开放封闭原则?

不完全是。

可以在不违反开闭原则的情况下使用受检异常。但是,向已“冻结”的 API 方法添加已检查异常确实违反了原则1。但是添加异常的行为才是违规行为……而不是异常本身,或一般的检查异常。


1 - 甚至这也是有争议的。反之,修复设计缺陷和错误并不违反开放封闭原则。

【讨论】:

"你对类的源代码所做的任何更改都违反了“封闭”部分",太严格了。 Bertrand Meyer 提出了明显的例外,例如"如果您对原始软件有控制权并且可以对其进行重写,使其能够满足多种客户的需求而不会造成额外的复杂性,那么您应该这样做。" 而且,"无论是开闭原则还是继承中的重定义都不是解决设计缺陷的方法,更不用说bug了。如果模块有问题,你应该修复它“。【参考方案2】:

不,检查的异常不违反OCP,原因很简单,OCP 适用于模块,而不是方法。

如果您认为已检查的异常只是方法签名的另一部分,那么这个问题与方法名称或方法参数或方法返回类型是否违反 OCP 相同。该原则根本不适用于这个粒度级别。

不知道方法是如何实现的,或者更重要的是,方法是如何通过其模块的 API 公开的,我们没有什么可判断的。例如,一个方法可能依赖于一个硬编码的常量;但如果该方法可以被客户端覆盖,它仍然可以扩展。没有任何关于已检查异常的存在告诉我们模块是否可扩展。

另一方面,如果检查的异常导致方法成为最终方法,并且如果该方法作为其公共 API 的一部分被模块公开,并且如果该模块没有提供该 API 的替代方案,那么它将是违反 OCP。

【讨论】:

这不是真的。 Meyer Bertrand (1988) 在他的《面向对象的软件构建》一书中指出“软件实体(类、模块、函数等)应该对扩展开放,但对修改关闭”。 请提供报价的页码。我正在看这本书 1997 年的第二版,我相信它取代了原版。 OCP 在第 57-61 页上定义。它在模块方面的定义是一致的。这句话更有可能来自罗伯特马丁 1995 年的文章,他在文章中解释了迈耶。 Martin 在 2014 年的链接博客文章中给出了更“成熟”的解释,该文章始终使用模块。

以上是关于检查异常是不是违反了开放封闭原则?的主要内容,如果未能解决你的问题,请参考以下文章

C#重新抽象是不是违反了开放/封闭原则?

开放封闭原则

Objective C 类别 vs 开放/封闭原则

开放/封闭原则的问题?

开放封闭原则|SOLID as a rock

设计模式--开放封闭原则