当前位置: 首页 > 知识库问答 >
问题:

当方法具有可选参数时,如何不违反Liskov替换原则?

宦文柏
2023-03-14

我在一个程序中遇到了一个设计问题,这是由于一个抽象有一个带有一个位置(因而是可选)参数的方法。

我们假设这个类是A,方法是vol f(x,[y]);。现在,y是可选的,因为我已经知道A的一些子类将使用它,其中一些不会。

实际的问题是违反Liskov替换原则:在需要y的子类中,如果没有提供y,我必须抛出异常,而在A. f(未实现)中,我没有抛出任何异常例外。

A的设计也很糟糕,因为我提供了一个方法f,有些子类确实需要它,有些子类需要稍微不同的版本。显然,我应该设计尽可能小的接口(接口隔离)。

这实际上是一个一般性的问题,不仅与Dart有关。

那么,如何处理可选参数以不违反Liskov替换原则?特别是,您将如何处理我的情况,以便我不会违反接口隔离原则?

我现在看到的唯一可行的解决方案(针对我的特定问题)是使当前的子类扩展A,并且实际需要f中的y来实际扩展(或者实现,如果A实际上是一个接口)另一个基类,方法是f(x,y),这两个参数都是必需的。但如何处理可选参数的问题仍然存在!

共有3个答案

暨宸
2023-03-14

利斯科夫替代原则的基本格言是:

使用指向基类的指针或引用的函数必须能够在不知道的情况下使用派生类的对象。

换句话说:-类应该基于行为而不是属性建模;-数据应基于属性而非行为建模。

所以在你的例子中,我会说这是一个违规,因为可选参数不是其余子类行为的一部分。最好将它作为使用它的子类的一部分。

这是一张来自神奇坚实原则的好照片!

姬墨竹
2023-03-14

我真的没有看到LSP和可选参数之间的联系。您将违反原则,这取决于您的代码,以及您对参数的处理,而不是因为语言提供给您的选项。

在您的示例中,我想说,您至少违反了LSP是有争议的,因为A是抽象的(因此您不能实例化它),并且您不能直接调用f,因为它没有实现。

让我们省略这一部分,假设您有类A和子类B(都是具体的),并且都实现了方法f(x,[y])

然后,您将a作为a的实例,将b作为b的实例。给定[xy]的任何值,如果在任何地方使用a.f(x,y),则可以使用((a)b)。f(x,y)得到完全相同的结果,则不违反LSP。

无论如何,如果你觉得你可能违反了LSP,那么你必须问问自己,你是否真的需要层次结构。与其将B声明为A的子类,也许您可以通过AB使用常用方法实现一个接口,并将B使用的A中的代码移动到另一个类,您可以从AB调用该类(请参见https://en.wikipedia.org/wiki/Composition_over_inheritance).

皇甫鸿远
2023-03-14

正如我所读到的,问题是您需要的子类实际上不能替代超类。您需要两个类,AB,以实现相同的API,即使这些类不是真正可交换的。其中一个只使用一个参数(可以说,应该只接受一个参数),而另一个需要两个参数。这两个类是不兼容的,因此添加一个公共超类来抽象不兼容的文件操作注定会失败。

也就是说,如果你已经知道A的一些子类不会使用第二个参数来foo,那么为什么它们是A的子类?因为作为A的子类,它们应该接受A接受的任何参数,并以与A. foo文档一致的方式使用它。

问题不是可选参数,而是超类中的可选参数。如果参数在超类中是可选的,那么它在所有子类中也必然是可选的,因为子类需要以与超类相同的方式可调用。接受(x,[y])的函数不能被接受一个或两个参数的函数所取代,相反。子类必须比超类允许更多,而不是更少,从可选参数到不可选参数允许更少。

如果你有课

class X { foo(x) {} }
class Y { foo(x, y) {} }
class Z implements X, Y { foo(x, [y]) {} }

然后它工作,因为Z允许超过XY。使用Z作为超类而不是子类是行不通的,这与可靠和安全的方向相反。

 类似资料:
  • 来自维基百科, Liskov的行为子类型概念定义了对象的可替代性概念;也就是说,如果S是T的子类型,则程序中T类型的对象可以替换为S类型的对象,而不改变该程序的任何期望属性(例如正确性)。 假设以下类层次结构: 基本抽象类-。它有一个只读属性,在后继程序中被重写。 基类的继承者-,它重写并返回灰色。 Cat的继任者-,它覆盖并返回带条纹的。 然后我们声明一个方法,参数类型为(不是)。 向该方法发送

  • 问题内容: 我的情况与Code Complete中Steve McConnell 提到的情况非常相似。我唯一的问题是基于车辆,而三轮车恰好是根据法律,属于汽车。到目前为止,汽车只有四个轮子。无论如何,我的域都不必要地复杂,因此很容易遵循下面的cats示例。 对重写例程并且在派生例程中不执行任何操作的类要保持怀疑。这通常表明基类的设计存在错误。例如,假设您有一个Cat类和一个例程Scratch(),

  • 我刚刚安装了Microsoft代码合同。这是的一部分。NET框架和Visual Studio加载项。它提供运行时检查和定义的合同的静态检查。 该工具有四个警告级别,因此我设置了最高级别。 我已经声明了违反Liskov替换原则的类。 LSP规定: 如果S是T的子类型,则T类型的对象可以替换为S类型的对象,而不改变该程序的任何期望属性 在我的例子中,违规行为是这样的:人=新孩子(23);。我们应该能够

  • 我正在阅读为什么Java中的数组协方差不好(为什么数组是协方差的,而泛型是不变的?)。如果是的子类型,则是的子类型。这是一个问题,因为这样的事情是可以做的: 这与“正确”实现的泛型不同。不是的子类型 我试图理解为什么它是坏的本质,并且刚刚读了关于LSP的文章。它有没有违反LSP?似乎没有明显的违规行为。

  • 我是OOP的新手。最近我读到了关于Liskov替换原理的文章。 在下面给出的代码中,Square类继承Give_区域。假设Square类与Square相关(比如有效性检查)。Give_Area给出正方形的面积(4个顶点位于圆的周长上)和圆的面积。所以,如果给我一个半径,我必须打印圆和正方形的面积(由放置在圆周长上的顶点组成)。为了得到圆的面积,我使用了一个参数。但在求平方面积时没有参数。因此,我在

  • 若我将public方法添加到子类中,并且客户端程序调用added方法,则客户端程序不能使用父对象而不是子类。 这个案子违反了LSP?