我读过一些关于将Square作为Rectangle类的继承类是不好的做法的文章,说这违反了LSP(Liskov替换原则)。我还是不明白,我用Ruby编写了一个示例代码:
class Rectangle
attr_accessor :width, :height
def initialize(width, height)
@width = width
@height = height
end
end
class Square < Rectangle
def initialize(length)
super(length, length)
end
def width=(number)
super(number)
@height = number
end
def height=(number)
super(number)
@width = number
end
end
s = Square.new(100)
s.width = 50
puts s.height
谁能告诉我它出了什么问题?
考虑抽象基类或接口(无论是接口还是抽象类,都是与LSP无关的实现细节)<代码> RealabrExcPosie;它具有只读属性宽度
和高度
。可以从中派生类型ReadableSquare
,该类型具有相同的属性,但合同保证宽度
和高度
始终相等。
从ReadableRectgle
中,可以定义具体类型ImmutableRectgle
(它在构造函数中具有高度和宽度,并保证Height
和Width
属性将始终返回相同的值),并且MutableRectgle
.我们还可以定义具体类型MutableRectgle
,它允许在任何时候设置高度和宽度。
在事物的“正方形”方面,ImmutableSquare
应该可以替代ImmutableRectangle
和ReadableSquare
。然而,MutableSquare
只能替代ReadableSquare
[后者反过来又可以替代ReadableRectangle
]此外,虽然不可变矩形
的行为可以替代不可变矩形
,但通过继承具体的不可变矩形
类型获得的值将受到限制。如果ImmutableRectangle
是一种抽象类型或接口,那么ImmutableSquare
类只需要使用一个字段而不是两个字段来保存其维度(对于一个有两个字段的类来说,保存一个字段没什么大不了的,但是不难想象类会有更多字段,这样可以节省很多)。但是,如果ImmutableRectangle
是一个具体类型,那么任何派生类型都必须具有其基的所有字段。
某些类型的正方形可以替代相应类型的矩形,但是可变正方形不能替代可变矩形。
从Liskov替换原理(LSP)的角度来看,它的错误在于,矩形
s和正方形
s是可变的。这意味着您必须显式地重新实现子类中的setter,并失去继承的好处。如果您使矩形
s不可变,即,如果您想要一个不同的矩形
,您可以创建一个新的矩形,而不是更改现有矩形的测量值,那么违反LSP就没有问题。
class Rectangle
attr_reader :width, :height
def initialize(width, height)
@width = width
@height = height
end
def area
@width * @height
end
end
class Square < Rectangle
def initialize(length)
super(length, length)
end
end
使用attr\u reader
提供getter而不是setter,因此具有不变性。在这种实现中,矩形
和正方形
都提供了对高度
和宽度
的可见性,对于正方形来说,它们总是相同的,面积的概念是一致的。
我并不总是喜欢Liskov,因为它似乎限制了基于行为而不是“本质”的继承。在我看来,继承总是意味着一种“是”的关系,而不是一种“行为完全一样”的关系。
话虽如此,维基百科的文章用你的确切例子详细说明了为什么有些人认为这是不好的:
违反LSP的一个典型示例是从矩形类派生的Square类,假设宽度和高度都存在getter和setter方法。
Square类始终假定宽度与高度相等。如果在预期为矩形的上下文中使用正方形对象,则可能会发生意外行为,因为不能(或者更确切地说不应该)单独修改正方形的尺寸。
这个问题不容易解决:如果我们可以修改Square类中的setter方法,使它们保持平方不变(即保持尺寸相等),那么这些方法将削弱(违反)矩形setter的后条件,即可以独立修改尺寸。
因此,在等效的矩形
代码旁边查看您的代码:
s = Square.new(100) r = Rectangle.new(100,100)
s.width = 50 r.width = 50
puts s.height puts r.height
输出左边是50,右边是100。
但是,在我看来,这是文章中重要的一点:
违反LSP,像这样,在实践中可能是也可能不是问题,这取决于使用违反LSP的类的代码实际期望的后置条件或不变量。
换句话说,只要使用类的代码理解行为,就没有问题。
总之,正方形是矩形的适当子集,因为矩形的定义足够宽松:-)
我启动了一个国际象棋项目,使用一些旧代码绘制地图,基本上所有内容都是复制粘贴的。问题是方块没有出现?我试着修了一会儿,但没有找到解决办法。下面可能是三种最重要的方法,并简要介绍了整个项目。有些是德语的。 https://drive.google.com/file/d/1nnZHLB0Ycy04eMyYbEmduMwbGhVLZ2VB/view?usp=sharing
本文向大家介绍关于C++中菱形继承和虚继承的问题总结,包括了关于C++中菱形继承和虚继承的问题总结的使用技巧和注意事项,需要的朋友参考一下 前言 菱形继承是多重继承中跑不掉的,Java拿掉了多重继承,辅之以接口。C++中虽然没有明确说明接口这种东西,但是只有纯虚函数的类可以看作Java中的接口。在多重继承中建议使用“接口”,来避免多重继承中可能出现的各种问题。本文将给大家详细介绍关于C++菱形继承
想改进这个问题吗?更新问题,使其仅通过编辑这篇文章来关注一个问题。 我有一个关于Java的问题,因为我了解不来用Java绘制几何图形,下面的代码你能帮我吗? 这是代码: 如何创建三角形、正方形和矩形的JFrame?更正我的代码谢谢
对于我正在编写的游戏,我在非正方形地图上使用四叉树。四叉树用于查找给定最大半径(圆)内的相邻单位的冲突检测、要攻击的敌人、最近的基地等。 我想知道的是,如果将四边形树由矩形而不是正方形制成,是否存在性能问题?矩形地图不是将正方形地图划分为正方形,而是在四边形树中划分为大小相等的矩形。 矩形地图上的方形四叉树:将创建一个四叉树,填充整个地图,但左侧或底部有空白/未使用区域,具体取决于地图的方向(水平
本文向大家介绍js绘制圆形和矩形的方法,包括了js绘制圆形和矩形的方法的使用技巧和注意事项,需要的朋友参考一下 本文实例讲述了js绘制圆形和矩形的方法。分享给大家供大家参考。具体如下: 这里使用js来绘制圆形和矩形,支持选择图形的背景颜色,同时可设置圆角矩形、半径、正圆、矩形、正方形这几个选项。或许这些图形你不需要,但重要的是让你学会JavaScript绘制图形的方法,这是要表达的核心。 运行效果
本文向大家介绍java用接口、多态、继承、类计算三角形和矩形周长及面积的方法,包括了java用接口、多态、继承、类计算三角形和矩形周长及面积的方法的使用技巧和注意事项,需要的朋友参考一下 本文实例讲述了java用接口、多态、继承、类计算三角形和矩形周长及面积的方法。分享给大家供大家参考。具体如下: 定义接口规范: 希望本文所述对大家的java程序设计有所帮助。