Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 为什么不可变对象允许遵循Liskov替换原则?_Java_Oop_Solid Principles - Fatal编程技术网

Java 为什么不可变对象允许遵循Liskov替换原则?

Java 为什么不可变对象允许遵循Liskov替换原则?,java,oop,solid-principles,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 setHe

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

    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中替换了
正方形
的一个实例而不是
矩形
,我的程序的行为将会改变。这与LSP相反

我听说,那个不可变的对象可以解决这个问题。但是为什么呢

我换了一个例子。 我在
矩形中添加构造函数

public Rectangle(int width, int height) {
this.width = width;
this.height = height;
}
然后,我换了二传手:

    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");
    }
}

}
同样的问题依然存在。更改对象本身还是返回新对象有什么区别。对于基类和子类,程序的行为仍然不同。

我抓住了这些引语(我的重点):

假设在
矩形
基类上有
SetWidth
SetHeight
方法;这似乎完全合乎逻辑。但是,如果您的
矩形
引用指向一个
正方形
,则
设置宽度
设置高度
没有意义,因为设置一个会改变另一个以匹配它。在这种情况下,
Square
未能通过使用
Rectangle
的Liskov替换测试,并且从
Rectangle
继承
Square
的抽象是一个坏的抽象

……还有

LSP指出的是子类型行为应该与基类型规范中定义的基类型行为相匹配。如果矩形基本类型规范说高度和宽度可以独立设置,那么LSP说正方形不能是矩形的子类型如果矩形规范说矩形是不可变的,那么正方形可以是矩形的子类型。这都是关于子类型维护为基类型指定的行为

我想如果你有这样一个构造器,它会工作的:

Square(int side){
    super(side,side);
    ...
}
因为没有办法改变不变的东西,所以没有设置器。正方形永远是正方形

但两者之间应该有一种不违反LSP的关系,这种关系也不会强迫您使用不可变对象。我们只是做错了

在数学中,正方形可以看作是矩形的一种。事实上,它是一种更为特殊的矩形。天真地说,这样做似乎合乎逻辑,因为矩形看起来很超级。但是拥有子类的目的不是为了创建现有类的较弱版本,而是为了增强功能

为什么不做一些像:

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);
 }
我抓住了这些引语(强调我的):

假设在
矩形
基类上有
SetWidth
SetHeight
方法;这似乎完全合乎逻辑。但是,如果您的
矩形
引用指向一个
正方形
,则
设置宽度
设置高度
没有意义,因为设置一个会改变另一个以匹配它。在这种情况下,
Square
未能通过使用
Rectangle
的Liskov替换测试,并且从
Rectangle
继承
Square
的抽象是一个坏的抽象

……还有

LSP指出的是子类型行为应该与基类型规范中定义的基类型行为相匹配。如果矩形基本类型规范说高度和宽度可以独立设置,那么LSP说正方形不能是矩形的子类型如果矩形规范说矩形是不可变的,那么正方形可以是矩形的子类型。这都是关于子类型维护为基类型指定的行为

我想如果你有这样一个构造器,它会工作的:

Square(int side){
    super(side,side);
    ...
}
因为没有办法改变不变的东西,所以没有设置器。正方形永远是正方形

但两者之间应该有一种不违反LSP的关系,这种关系也不会强迫您使用不可变对象。我们只是做错了

在数学中,正方形可以看作是矩形的一种。事实上,它是一种更为特殊的矩形。天真地说,这样做似乎合乎逻辑,因为矩形看起来很超级。但是拥有子类的目的不是为了创建现有类的较弱版本,而是为了增强功能

为什么不做一些像:

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);
 }
问题在于合同,即使用
矩形
的程序员的期望

合同规定,您可以执行一个
setWidth(15)
,然后
getWidth()
将返回
15
,直到您使用不同的值执行另一个
setWidth

setHeight
的角度来看,这意味着它不能更改
height
。继续这种思路,setter的约定是“将此属性更新为参数值,并保持所有其他属性不变”

现在在
Square
中,新的不变量
getWidth()==getHeight()
强制
setWidth
也设置高度,瞧:违反了
setHeight
的约定

当然,您可以在
Rectangle.setWidth()
的契约(即方法文档)中明确声明,如果调用
setHeight()
,宽度可能会改变。
但是现在你的