我听说Liskov替换原则(LSP)是面向对象设计的基本原则.它是什么以及它的使用例子是什么?
oop liskov-substitution-principle definition design-principles solid-principles
请参阅System.Array类的定义
public abstract class Array : IList, ...
Run Code Online (Sandbox Code Playgroud)
从理论上讲,我应该能够写下这一点,并感到高兴
int[] list = new int[] {};
IList iList = (IList)list;
Run Code Online (Sandbox Code Playgroud)
我也应该可以从iList中调用任何方法
ilist.Add(1); //exception here
Run Code Online (Sandbox Code Playgroud)
我的问题不是为什么我得到一个例外,而是为什么Array实现了IList?
你能用一个很好的C#例子解释Liskov替换原理(SOLID的'L'),以简化的方式涵盖原理的所有方面吗?如果真的有可能.
背景:
作为Java程序员,我从接口广泛继承(而不是:实现),有时我设计抽象基类.但是,我从来没有真正觉得需要子类化一个具体的(非抽象)类(在我这样做的情况下,后来发现另一个解决方案,例如委托会更好).
所以现在我开始觉得几乎没有从具体类继承的合适的情况.一方面,Liskov替换原则(LSP)似乎几乎不可能满足非平凡类; 还有许多其他问题,这里似乎呼应了类似的意见.
所以我的问题:
在哪种情况下(如果有的话)从具体类继承它真的有意义吗?你能给出一个继承自另一个具体类的类的具体的,真实的例子吗?你觉得这是给定约束的最佳设计吗?我对满足LSP的示例(或满足LSP似乎不重要的示例)特别感兴趣.
我主要有Java背景,但我对任何语言的例子感兴趣.
我通常会尝试确保我的对象实例符合Liskov替换原则,但我一直想知道人们是否认为LSP也应该适用于构造函数?
我已经尝试使用谷歌搜索,但无论如何我都无法找到任何强烈的意见.
我应该注意到我的大部分编码都是在Ruby中,但我有时会发现我的子类构造函数与父类略有不同.它们使用相同的基本参数集,通常是额外的参数.有时这也会发生在其他类方法中.
在我的脑后,这总是感觉像LSP违规,但我想看看是否有其他人也有这种感觉.
我是设计和学习设计原则的新手.
它说从矩形推导出方形是违反Liskov替代原则的典型例子.
如果是这样的话,那么正确的设计应该是什么?
我对Liskov替换原则的理解是,对于派生类,基类的某些属性是真的或某些实现的基类行为.
我想这意味着当一个方法在基类中定义时,它永远不应该在派生类中被覆盖 - 因为那么替换基类而不是派生类会产生不同的结果.我想这也意味着,拥有(非纯)虚拟方法是件坏事吗?
我想我可能对这个原则有错误的理解.如果我不这样做,我不明白为什么这个原则是好的做法.谁可以给我解释一下这个?谢谢
liskov-substitution-principle design-principles solid-principles
鉴于以下课程:
class Example implements Interface1, Interface2 {
...
}
Run Code Online (Sandbox Code Playgroud)
当我使用Interface1以下方法实例化类时:
Interface1 example = new Example();
Run Code Online (Sandbox Code Playgroud)
...然后我只能调用Interface1方法,而不是Interface2方法,除非我施放:
((Interface2) example).someInterface2Method();
Run Code Online (Sandbox Code Playgroud)
当然,为了使这个运行时安全,我还应该用一个instanceof检查包装它:
if (example instanceof Interface2) {
((Interface2) example).someInterface2Method();
}
Run Code Online (Sandbox Code Playgroud)
我知道我可以有一个扩展两个接口的包装器接口,但最后我可能会有多个接口来满足可以由同一个类实现的所有可能的接口排列.有问题的接口并不会自然地相互扩展,因此继承似乎也是错误的.
instanceof/ cast方法是否会破坏LSP,因为我正在询问运行时实例以确定其实现?
无论我使用哪种实现,似乎都会在糟糕的设计或使用中产生一些副作用.
鉴于此代码:
trait Base {
fn a(&self);
fn b(&self);
fn c(&self);
fn d(&self);
}
trait Derived : Base {
fn e(&self);
fn f(&self);
fn g(&self);
}
struct S;
impl Derived for S {
fn e(&self) {}
fn f(&self) {}
fn g(&self) {}
}
impl Base for S {
fn a(&self) {}
fn b(&self) {}
fn c(&self) {}
fn d(&self) {}
}
Run Code Online (Sandbox Code Playgroud)
不幸的是,我不能投&Derived给&Base:
fn example(v: &Derived) {
v as &Base;
}
Run Code Online (Sandbox Code Playgroud)
error[E0605]: non-primitive cast: `&Derived` as `&Base`
--> …Run Code Online (Sandbox Code Playgroud) Liskov替换原则是SOLID的原则之一.我现在已经多次阅读过这个原则,并试图理解它.
这是我用它做的,
这一原则与阶级等级之间强烈的行为契约有关.子类型应该能够在不违反合同的情况下用超类型替换.
我也读过其他一些文章,我有点想不起这个问题.千万Collections.unmodifiableXXX()方法不违反LSP?
以上链接文章的摘录:
换句话说,当通过其基类接口使用对象时,用户只知道基类的前提条件和后置条件.因此,派生对象不能指望这样的用户遵守比基类所要求的更强的前提条件
之前
class SomeClass{
public List<Integer> list(){
return new ArrayList<Integer>(); //this is dumb but works
}
}
Run Code Online (Sandbox Code Playgroud)
后
class SomeClass{
public List<Integer> list(){
return Collections.unmodifiableList(new ArrayList<Integer>()); //change in implementation
}
}
Run Code Online (Sandbox Code Playgroud)
我SomeClass将来无法改变返回不可修改列表的实现.编译将工作,但如果客户端以某种方式尝试更改List返回,那么它将在运行时失败.
这就是为什么Guava 为集合创建了单独的ImmutableXXX接口?
这不是直接违反LSP或我完全弄错了吗?
liskov-substitution-principle ×10
oop ×6
java ×3
c# ×2
.net ×1
arrays ×1
class-method ×1
collections ×1
constructor ×1
definition ×1
ilist ×1
inheritance ×1
rust ×1