Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/dart/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Oop 当方法具有可选参数时,如何不违反Liskov替换原则?_Oop_Dart_Solid Principles_Liskov Substitution Principle - Fatal编程技术网

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

Oop 当方法具有可选参数时,如何不违反Liskov替换原则?,oop,dart,solid-principles,liskov-substitution-principle,Oop,Dart,Solid Principles,Liskov Substitution Principle,我在一个程序中遇到了一个设计问题,这是由于一个抽象基类有一个带有一个位置(因而是可选)参数的方法 假设这个类是A,方法是void f(x[y])。现在,y是可选的,因为我已经知道A的一些子类将使用它,而其中一些子类将不使用它 实际问题是违反了Liskov替换原则:在需要y的子类中,如果未提供y,我必须抛出异常,而在a.f(未实现)中,我没有抛出任何异常 A的设计也很糟糕,因为我提供了一个方法f,有些子类确实需要它,有些子类需要稍微不同的版本。显然,我应该设计尽可能小的接口(接口隔离) 这实际上是

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

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

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

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

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

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


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

我真的看不到LSP和可选参数之间的联系。您将违反这一原则,这取决于您的代码,而您对参数所做的操作并不是因为语言提供给您的选项

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

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

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

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

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

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

问题不是可选参数,而是超类中的可选参数。如果参数在超类中是可选的,那么它在所有子类中也必然是可选的,因为子类需要以与超类相同的方式调用。接受
(x[y])
的函数不能替换为只接受一个或两个参数的函数,反之亦然。 子类必须允许多于父类,而不是少于父类,并且从可选参数变为非可选参数的情况下,允许的数量会减少

如果你有课

类X{foo(X){}
类Y{foo(x,Y){}
类Z实现X,Y{foo(X,[Y]){}

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

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

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

换言之: -类应基于行为建模,而不是基于属性建模; -数据建模应基于属性而非行为

因此,在您的情况下,我认为这是一种违反,因为可选参数不会成为其他子类行为的一部分。最好将它作为使用它的子类的一部分

这是一张来自《惊艳世界》的好照片


参数x和y有什么共同点吗?也许你可以用一种新的方式把它们“打包”在一起。这样的话,
f
@JanezKuhar只有一个参数,我也考虑过这个选项,但是创建另一个类型只是为了解决这个参数问题,这看起来像是一个黑客,因为问题是