问题标签 [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.
php - 根据 Liskov 替换原则,是否允许子类具有公共方法?
考虑以下类层次结构:
现在这个类LaserPrinter
不能被它的父类替换,Printer
因为Printer
它没有setFile
方法。
这是否意味着他的等级制度违反了 Liskov 替代原则?不允许子类有自己的公共方法吗?
java - 对 Liskov 替换原则的合规性进行单元测试是一种好习惯吗?
假设一个名为 的类Sprinter
:
和一个SprintGenius
类型继承Sprinter
:
从逻辑上讲,必须创建 2 个单元测试类,每种类型一个。
在Sprinter
单元测试中,我最终会得到:
在SprintGenius
单元测试中,我最终会得到:
在上面的两个测试中,我都会在 10 秒内测试行进的米数。
显然,这两个测试都是绿色的。
但是,违反里氏替换原则怎么办?
事实上,任何客户端代码都应该期望任何短跑运动员在 9 秒内跑完 10 米。
3 个解决方案(前两个解决方案向所有团队的开发人员发出了规则,即使不是每个人都能很好地掌握 Liskov 的概念,也必须承认并保留)
1) 在Sprinter
课堂上,重复每个测试,但这次基于 aSprinter sprinter = new SuperGenius()
并期望 90 米。=> 什么应该失败,这正是我们想要的!=> 防止违反 Liskov 原则。
2)在SprintGenius
类中,始终基于完全相同的期望添加基于基类的每个测试的类似“克隆”。所以,如果你有 2 个不同的测试,我们最终会得到 4 个测试。2 将 Sprinter 声明为 aSprinter
和 2 声明Sprinter
为SprintGenius
.
3)从不继承具体类(我想这是你阅读这篇文章的第一反应:)),如果合适的话,更喜欢组合!这样这个问题就不会发生了。
基于许多开发人员忽略 Liskov 原则并且经常试图从具体类继承而不是使用其他更好的方法(如组合或不同的继承层次结构)这一事实,防止违反 Liskov 替换原则的最佳实践是什么?
我不想因为开发人员从我的书面课程继承(没有告诉我..),将其注入到一个共享的巨大异构Sprinter
列表列表中并以“你好,奇怪的行为!”而给我带来麻烦。和数小时的调试时间......
我当然不想宣布我所有的具体类“最终”:)
c# - ReadOnlyCollection vs Liskov - 如何正确建模可变集合的不可变表示
Liskov-substitution 原则要求子类型必须满足超类型的契约。据我了解,这将ReadOnlyCollection<T>
违反 Liskov。 ICollection<T>
的合约暴露Add
和Remove
操作,但是只读子类型不满足这个合约。例如,
显然需要不可变的集合。.NET 的建模方式有问题吗?更好的方法是什么? IEnumerable<T>
在暴露集合方面做得很好,至少看起来是不可变的。然而,语义是非常不同的,主要是因为IEnumerable
没有显式地暴露任何状态。
在我的特殊情况下,我正在尝试构建一个不可变的DAG类来支持FSM。一开始我显然需要AddNode
/AddEdge
方法,但我不希望一旦它已经运行就可以更改状态机。我很难表示 DAG 的不可变和可变表示之间的相似性。
现在,我的设计涉及预先使用 DAG Builder,然后创建一次不可变图,此时它不再可编辑。Builder 和具体的不可变 DAG 之间唯一的通用接口是Accept(IVisitor visitor)
. 我担心面对可能更简单的选项,这可能是过度设计/过于抽象。同时,我无法接受我可以在我的图形接口上公开方法,NotSupportedException
如果客户端获得特定的实现,这些方法可能会抛出。处理这个问题的正确方法是什么?
c# - SOLID Essentials 缺点?
我已经阅读了很多关于此的文章,但我仍然有 2 个问题。
问题 #1 - 关于依赖倒置:
它指出高级类不应该依赖于低级类。两者都应该依赖于抽象。抽象不应该依赖于细节。细节应该取决于抽象。
例如 :
修复:把它放在一个ctor中。
如果它将在 ctor 中 - 我每次使用课程时都必须发送它。
BirthdayCalculator
所以我在上课时必须保留它。这样做可以吗?我可以争辩说,在修复之后,仍然 -
IList<Birthday> _birthdays
不应该在那里(Birthday
在IList
) - 但它应该是IList<IBirthday>
。我对吗 ?
问题 #2 - 关于Liskov 替换:
派生类必须可以替代它们的基类
或更准确:
令 q(x) 是关于 T 类型的对象 x 的可证明性质。那么 q(y) 对于 S 类型的对象 y 应该为真,其中 S 是 T 的子类型。
(已经读过这个)
例子 :
我有一堂课:
银行想开一个抵押账户——所以:
当有一个函数接受抵押 Account
并做存款时,问题就出现了。
所以在这里,它违反了 LSP。
但 我不明白。
每个被覆盖的方法在被覆盖时都会执行不同的代码,因此它永远不会100 %可替换!
定义没有谈论“逻辑应该像基类一样继续(总是存入(添加)正数)”
例子:
如果CheckingAccount
class 和MortgageAccount
class 都存入正数但MortgageAccount
也登录到 db 怎么办?它仍然会破坏 LSP 吗?休息/不刹车 LSP 的边界是什么?
定义应该定义什么是边界。它并没有说什么。
我错过了什么?
java - 有没有一种方法可以使用委托设计模式而不会失去 Java 中的可替代性?
这个问题是指在此处找到的委托设计模式。
我的游戏引擎有许多接口代表各种实体:
- 播放器
- 车辆
- 网
- 等等
并且每一个都可以渲染,因此它们实现了包含方法的 Renderable 接口render()
。
方法一
使用委托,示例如下所示:
每当我想渲染一辆车时,我只需调用car.renderable.render();
.
这种方法的问题是我无法创建一个 List 并遍历它。
方法二
为了解决这个问题,我可以让 Vehicle 扩展 Renderable:
但问题在于,如果我定义 Car、Bicycle、Truck、Tank 等,这些类中的每一个都必须填写 render() 的代码(这可能是相同的)。
有没有办法保持在我的 Vehicle 接口中扩展 Renderable 的好处,而不必在所有实现 Vehicle 的具体类中定义 render() ?
c# - How to comply with Liskov's Substitution Principle (LSP) and still benefit from polymorphism?
The LSP says "The derived types must not change the behavior of the base types", in other words "Derived types must be completely replaceable for their base types."
This means that if we define virtual methods in our base classes, we have violated this principle.
Also if we hide a method in the drive method by using new keyword then again we have violated this principle.
In other words, if we use polymorphism we have violated LSP!
In many applications I've used Virtual methods in the base classes and now I realize it violates LSP. Also if you use Template Method pattern you have violated this principle that I've used it a lot.
So, how to design your application that complies with this principle when you'd need inheritance and you'd like to benefit also from polymorphism? I'm confused!
See the example from here: http://www.oodesign.com/liskov-s-substitution-principle.html
c# - 打破 Liskov 替换原则的类使用陷阱
在我最近工作的一个项目中,注意到一些接受属于层次结构的类的方法具有类似于以下的代码:
好吧,这让我觉得这是对LSP的公然违反,因为现在如果您在生产代码、单元测试模拟或依赖注入拦截器的一部分中使用狗的子类,则此代码将不会以相同的方式工作。我相信通过将条件更改为:
这让我想到了:
是否还有其他可以在客户端代码中破坏 LSP 的陷阱?
更新
为了澄清起见,我正在寻找在层次结构中使用类的代码中可能存在的缺陷。我知道并且我不是在寻找结构严重的层次结构的问题 - (例如矩形 - 正方形问题)。我试图找出要查找的内容,以确保代码将支持动态模拟、拦截器、装饰类(例如 LoggingDog),就像它处理原始类一样。
到目前为止,在浏览完答案和链接之后,我可以看到唯一的陷阱是直接使用类的类型——即直接使用GetType()
方法或通过其他一些技术。尽管这里有一些注释is
和as
操作符,在这种情况下,甚至转换为基类型也不会破坏 LSP,因为子类的评估方式与原始类的评估方式相同。
oop - 如果 Address 继承自 PhoneNumber,它违反了哪些 OOP 原则?
有一本书说有一个PhoneNumber
类,然后我们会定义一个Address
继承自的类,PhoneNumber
我曾经说过,我们不能这样做,因为地址不是电话号码,并且要继承,它必须是“是”关系。如:狗是一种动物,我们可以Dog
继承自Animal
.
但是既然我们必须遵循LSP -- Liskov Substitution Principle,那么“是一个”规则实际上并不是这里的决定因素,因为一个正方形“是一个”矩形(宽度 == 高度),但是 LSP 说我们可以' t 定义一个Square
类并从该类继承Rectangle
。简单的英文解释,我想,就是对象aRect
可以响应消息setWidthAndHeight(w, h)
,但aSquare
不能正确响应,让整个程序正常运行。
如此令人惊讶的是,Address
继承类的PhoneNumber
类违反了“is a”关系,但并不违反 LSP。那么正式地,它违反了哪些 OOP 原则?
software-design - 避免 LSP(Liskov 替换原则)违规
我目前正在阅读Michael Feathers 的“有效地使用遗留代码”
我想我了解 LSP 违规,但问题是它说明了有助于避免 LSP 违规的经验法则,
- 尽可能避免覆盖具体方法。
- 如果这样做,请查看是否可以在覆盖方法中调用您正在覆盖的方法。
我不太明白数字2,你能帮我澄清一下吗?
c++ - 为什么函数指针会破坏可替代性而 std::function 不会?
我们有一个通常的类层次结构:
还有一个接受一个参数的函数 - 对基类对象的引用。
然后,我想创建类型的函数指针void (D&)
并存储b_set
在其中。这应该是有效的操作,因为合法传递给函数指针调用的所有对象也必须是 B 类型。但这是不允许的。
所以...
- 那无效转换如何?
- 为什么无效转换?
- 如果允许这样做会破坏什么?
- std::function 首先是如何避免这个问题的?