问题标签 [liskov-substitution-principle]
For questions regarding programming in ECMAScript (JavaScript/JS) and its various dialects/implementations (excluding ActionScript). Note JavaScript is NOT the same as Java! Please include all relevant tags on your question; e.g., [node.js], [jquery], [json], [reactjs], [angular], [ember.js], [vue.js], [typescript], [svelte], etc.
aggregate - 扩展基类的聚合类 - 违反 LSP?
维基百科上的里氏替换原则(LSP)
假设我有一个Alien
带有numFingers
属性*的类。有时,我需要numFingers
从数据库中提取按其他字段值分组的总和。在这些情况下,我不需要单独操作每条记录,但我确实需要访问它们的许多功能——能够获取属性、对它们执行一些基本逻辑等。这可能包括从数千个记录中汇总的数据记录,因此Alien
当数据库查询可以为我完成求和工作时,实例化数千个对象几乎没有意义。
我想创建一个名为 的扩展类AlienAggregate
,其属性是从分组和求和查询中设置的。这个类将允许我调用任何Alien
方法。这两个类的功能之间的唯一区别是GetID()
. 聚合类没有 ID,因为它的数据来源于任意数量的记录。因此,调用GetID()
onAlienAggregate
会引发异常。
这是否违反了 Liskov 替换原则?有没有更好的方法来处理呼叫GetID()
?Alien
有没有更好的方法来设计和类之间的关系AlienAggregate
?
*实际名称可能已更改,只是因为我可以。
inheritance - 为什么将实例声明为超类型却将其实例化为子类型,加上里氏替换原则
几天来,我一直在尝试理解 Liskov 替换原则,在使用非常典型的 Rectangle/Square 示例进行一些代码测试时,我创建了下面的代码,并提出了 2 个关于它的问题。
问题 1:如果我们有超类/子类关系,为什么我们要将一个实例声明为超类型,但将其实例化(新建)为子类型?
我理解为什么,如果我们通过接口进行多态性,我们希望以这种方式声明和实例化变量:
但是,现在我在旧的编程类和一些博客示例中回忆起它,当通过继承使用多态性时,我仍然会看到一些示例,其中一些代码会以这种方式声明变量
在我下面的代码中,Square 继承自 Rectangle,所以当我以这种方式创建一个新的 Square 实例时:
它仍然可以被视为一个矩形,或者添加到一个通用的矩形列表中,那么为什么有人仍然想将它声明为 Rectangle = new Square() 呢?是否有我没有看到的好处,或者需要这样做的场景?就像我说的,我下面的代码工作得很好。
}
尽管这应该违反 Liskov 替换原则,但我得到以下输出:
"宽度:90,高度:80
ConsoleApp.Program+矩形
宽度:80,高度:80
ConsoleApp.Program+Square
宽度:80,高度:80 ConsoleApp.Program+Square
问题 2:那么,此代码示例为何或如何破坏 LSP?仅仅是因为所有边相等的 Square 不变量打破了 Rectangle 不变量,边可以独立修改吗?如果是这个原因,那么 LSP 违规只是理论上的吗?或者,在代码中,我怎么能看到这个代码违反了原则?
编辑:在我正在阅读的一篇 LSP 博客文章中提出了第三个问题,但没有答案,所以就是这个
问题 3:开闭原则指出我们应该通过新的类(继承或接口)引入新的行为/功能。因此,例如,如果我在基类中有一个 WriteLog 方法,它没有先决条件,但是我引入了一个新的子类,它覆盖了该方法,但只有在事件非常关键时才实际写入日志....如果这是新的预期功能(对子类型进行强化的前提条件),这仍然会破坏 LSP 吗?在这种情况下,这两个原则似乎相互矛盾。
提前致谢。
design-patterns - 适配器模式与 Liskov 替换
适配器设计模式用于将类的接口(目标)转换为客户期望的另一个接口(适配器)。Adapter 让不兼容的类可以一起工作,因为它们的接口不兼容。
适配器模式可以通过两种方式实现,继承(适配器模式的类版本)和组合(适配器模式的对象版本)。
我的问题是关于使用继承实现的适配器模式的类版本。
这是绘图编辑器的示例:
我们想复用TextView类来实现TextShape,但是接口不同,所以TextView和Shape对象不能互换使用。
是否应该更改 TextView 类以符合形状界面?也许不是。
TextShape 可以通过以下两种方式之一使 TextView 界面适应形状的界面:
- 通过继承Shape的接口和TextView的实现(Adapter模式的类版)
- 通过在 TextShape 对象内部组成一个 TextView 实例,并使用 TextView 实例(适配器模式的对象版本)实现 TextShape 的接口。
类适配器
现在的问题:-)。TextShape 是否继承自 Shape 尤其是 TextView 是有效的“是”关系?如果不是,这是否违反了Liskov 的替代原则?
oop - 关于LSP(Liskov Substitution Principle)和子类型的问题
LSP说
如果 q(x) 是关于 T 类型的对象 x 的可证明属性,那么 q(y) 应该对 S 类型的对象 y 为真,其中 S 是 T 的子类型。
我可以改写如下:
q(x) 对 T 的任何 x 为真 => q(y) 对 T 的任何子类型的任何 y 为真
现在另一个声明呢?
q(x) 对 T 的任何 x 都为真,并且 q(y) 对 S 的任何 y 都为真 => S 是 T 的子类型
是否有意义 ?我们可以用它作为 的定义吗subtype
?
constructor - 构造函数应该遵守里氏替换原则吗?
我通常会尝试确保我的对象实例符合Liskov Substitution Principle,但我一直想知道人们是否认为 LSP 也应该适用于构造函数?
我试过用谷歌搜索,但无论哪种方式,我都找不到任何强烈的意见。
我应该注意,我的大部分编码都是用 Ruby 编写的,但有时我会发现我的子类构造函数与父类略有不同。它们采用相同的基本参数集,通常还有额外的参数。有时,其他类方法也会发生这种情况。
在我的脑海里,这一直感觉像是违反了 LSP,但我想看看其他人是否也有这种感觉。
java - 我的讲师对 Liskov 替换原则的定义是不正确的,还是我误解了?
由于(Liskov)替换原则,以下内容确实有效,该原则表示,如果某个类的实例需要引用,那么您可以替换对该类的任何子类的实例的引用。
现在,据我了解,在这种情况下,我正在创建一个Cat
对象(因此正在堆中创建内存空间),然后我将一个名为“felix”的对象引用变量分配给新创建的Cat
对象。引用变量的类型是Cat
,因此只能控制Cat
及其任何子类Cat
。
然后我创建一个Object
type 的引用变量Object
,并将其指向 felix ( Cat
) 对象,该对象可以工作但功能有限,因为 JVM 现在看到 felix 对象的 type Object
,因此,例如,如果purr()
在Cat
类,felix 将无法再使用它。
所以 typeCat
应该有一个引用,但是我们为 cat 类型的超类(而不是上面定义中所说的子类)提供了一个引用,这是允许的,但功能有限(除非你进行强制转换)。
我是正确的还是错误的?
c# - 为什么数组实现IList?
参见System.Array类的定义
理论上我应该能写到这点并且开心
我也应该能够从 iList 调用任何方法
我的问题不是为什么我得到一个异常,而是为什么 Array 实现 IList?
stack - 堆栈、有界堆栈和 Liskov 替换属性
有界堆栈数据结构(具有上限的堆栈)能否在不违反 Liskov 替换属性的情况下实现为传统堆栈的子类型?
可以使用常规堆栈来代替有界堆栈,但是如果有界堆栈具有足够大的限制,则只能使用有界堆栈来代替常规堆栈。我对这个想法正确吗?
liskov 属性是否相反?
谢谢。
java - 抽象属性是否违反了 Liskov 替换原则?
假设我有一个抽象类,例如:
我的程序会Pet
根据是否设置特殊处理标志来区别对待 s。我的问题是这是否算作违反了 Liskov 替代原则,该原则指出:
[...] 在计算机程序中,如果 S 是 T 的子类型,那么 T 类型的对象可以被 S 类型的对象替换 [...]而不会改变该程序的任何期望属性(正确性,执行的任务), ETC。)。
java - 继承和 LSP
提前为一个冗长的问题道歉。反馈特别感谢这里。. .
在我的工作中,我们对日期范围做了很多事情(如果你愿意的话,日期期间)。我们需要进行各种测量,比较两个日期期间之间的重叠等。我设计了一个接口、一个基类和几个派生类,它们可以很好地满足我的需求:
- 日期期间
- 日期期间
- 日历月
- 日历周
- 财政年度
DatePeriod 超类的基本要素如下(省略了所有令人着迷的特性,这些特性是我们需要这组类的基础......):
(Java 伪代码):
基类包含一组相当专门的方法和属性,用于操作日期周期类。派生类仅更改设置相关期间的起点和终点的方式。例如,对我来说,CalendarMonth 对象确实“是一个”DatePeriod 是有意义的。但是,出于显而易见的原因,日历月具有固定的持续时间,并且具有特定的开始日期和结束日期。事实上,虽然 CalendarMonth 类的构造函数与超类的构造函数相匹配(因为它具有 startDate 和 endDate 参数),但这实际上是简化构造函数的重载,它只需要一个 Calendar 对象。
对于 CalendarMonth,提供任何日期都将生成一个 CalendarMonth 实例,该实例从相关月份的第一天开始,到该月的最后一天结束。
为冗长的序言道歉。鉴于上述情况,这种类结构似乎违反了 Liskov 替换原则。虽然可以在任何可能使用更通用的 DatePeriod 类的情况下使用 CalendarMonth 的实例,但关键方法的输出行为会有所不同。换句话说,必须意识到在给定情况下正在使用 CalendarMonth 的实例。
虽然 CalendarMonth(或 CalendarWeek 等)遵守通过基类使用 IDatePeriod 建立的合同,但在使用 CalendarMonth 并且预期普通旧 DatePeriod 的行为的情况下,结果可能会变得非常扭曲。. . (请注意,在基类上定义的所有其他时髦方法都可以正常工作 - 只有开始和结束日期的设置在 CalendarMonth 实现中有所不同)。
有没有更好的方法来构建它,以便在不影响可用性和/或重复代码的情况下保持对 LSP 的正确遵守?