java - 为什么不可变对象(immutable对象)允许遵守里氏替换原则?

标签 java oop solid-principles

我想展示一个矩形和正方形的示例:

    class Rectangle {

private int width;
private int height;

public int getWidth() {
    return width;
}

public void setWidth(int width) {
    this.width = width;
}

public int getHeight() {
    return height;
}

public void setHeight(int height) {
    this.height = height;
}

public int area() {
    return width * height;
}

}

class Square extends Rectangle{

@Override
public void setWidth(int width){
    super.setWidth(width);
    super.setHeight(width);
}

@Override
public void setHeight(int height){
    super.setHeight(height);
    super.setWidth(height);
}

}

public class Use {

public static void main(String[] args) {
    Rectangle sq = new Square();
    LSPTest(sq);
}

public static void LSPTest(Rectangle rec) {
    rec.setWidth(5);
    rec.setHeight(4);

    if (rec.area() == 20) {
        // do Something
    }
}

}

如果我在 LSPTest 方法中替换 Square 实例而不是 Rectangle,我的程序的行为将会改变。这与LSP相反。

我听说,不可变对象(immutable对象)可以解决这个问题。但为什么呢?

我改变了例子。 我在矩形中添加构造函数:

public Rectangle(int width, int height) {
this.width = width;
this.height = height;
}

然后,我更改了 setter :

    public Rectangle setWidth(int width) {
    return new Rectangle(width, this.height);
}

public Rectangle setHeight(int height) {
    return new Rectangle(this.width, height);
}

现在,Square 看起来像:

class Square{
public Square() {

}

public Square(int width, int height) {
    super(width, height);
}

@Override
public Rectangle setWidth(int width) {
    return new Rectangle(width, width);
}

@Override
public Rectangle setHeight(int height) {
    return new Rectangle(height, height);
}

}

这是客户端代码:

 public class Use {

public static void main(String[] args) {
    Rectangle sq = new Square(4, 4);
    LSPTest(sq);
}

public static void LSPTest(Rectangle rec) {
    rec = rec.setHeight(5);

    if (rec.area() == 20) {
        System.out.println("yes");
    }
}

}

同样的问题仍然存在。对象本身改变或者返回一个新对象有什么区别。对于基类和子类,程序的行为仍然不同。

最佳答案

From here我捕获了这些引言(强调我的):

Imagine you had SetWidth and SetHeight methods on your Rectangle base class; this seems perfectly logical. However if your Rectangle reference pointed to a Square, then SetWidth and SetHeight doesn't make sense because setting one would change the other to match it. In this case Square fails the Liskov Substitution Test with Rectangle and the abstraction of having Square inherit from Rectangle is a bad one.

...还有...

What the LSP indicates is that subtype behavior should match base type behavior as defined in the base type specification. If the rectangle base type spec says that height and width can be set independently, then LSP says that square cannot be a subtype of rectangle. If the rectangle spec says that a rectangle is immutable, then a square can be a subtype of rectangle. It's all about subtypes maintaining the behavior specified for the base type.

我想如果你有一个像这样的构造函数,它会起作用:

Square(int side){
    super(side,side);
    ...
}

因为没有办法改变不可变的东西,所以没有 setter 。正方形永远是正方形。

但是两者之间应该有可能存在不违反 LSP 的关系,也不会强制您使用不可变对象(immutable对象)。我们只是做错了。

在数学中,正方形可以被视为矩形的一种。事实上,它是一种更具体的矩形类型。天真地说,Square extends Rectangle 似乎很合乎逻辑,因为矩形看起来非常super。但拥有子类的目的不是创建现有类的较弱版本,而是应该增强功能。

为什么不拥有类似的东西:

class Square{
   void setSides(int side);
   Boundary getSides();
}
class Rectangle extends Square{
    //Overload
    void setSides(int width, int height);
    @Override
    Boundary getSides();
}
<小时/>

我还想指出Setter用于设置。下面的代码很糟糕,因为您实际上创建了一个方法,但它不会按照它所说的那样执行操作。

 public Rectangle setWidth(int width) {
    return new Rectangle(width, this.height);
 }

关于java - 为什么不可变对象(immutable对象)允许遵守里氏替换原则?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36378874/

相关文章:

c# - 移除开关盒的设计模式

swift - 尝试使用依赖倒置存储值时崩溃

c# - 一个 child 实现接口(interface)而另一个 child 不实现接口(interface)是否违反了里氏替换原则?

python - 添加我的类的两个实例

php - 与 OOP 概念斗争

java - 选择 X 类 ArrayList 的不同字段

java - 从同一行读取多个输入。

asp.net - 如何有效使用DTO对象(数据传输对象)?

java - Java中如何指定文件路径?

java - JPA 序列生成器 : All generators in single table