長方形有二個屬性長和寬。並有一個設置長的方法和設置寬的方法,還有一個求面積的方法.
像下面
private int length;
private int width;
public void setLength(int lenght) {
this.length = lenght;
}
public void setWidth(int width) {
this.width= width;
}
public int getArea() {
return this.length * this.width;
}
如果說正方形是長方形的子類。為了保證正方形長和寬相等,那對應於正方形的二設置長寬的個方法就得改成
public void setLength(int lenght) {
this.length = lenght;
this.width= lenght;
}
public void setWidth(int width) {
this.length = width;
this.width= width;
}
那我們想想用戶使用時候的情景。 我們都知道長方形的面積等於長與寬的積。那當我們用長方形的時候我們會這樣用
Rectangle rectangle = new Rectangle();
rectangle.setLength(5);
rectangle.setWidth(4);
我們想知道面積是多少我們就可以
rectangle.getArea();
得到的是20,當然結果是非常正確的。
但想想如果我們把一個正方形的實例給用戶用的時候會怎么樣
Rectangle rectangle = new Square(); //注意,這里體顯代換原則。用戶根本不知道真正的實例是正方形,用戶只知道長方形的事情。
rectangle.setLength(5);
rectangle.setWidth(4);
我們想知道面積是多少我們就可以
rectangle.getArea();
得到的結果卻是 16 ,這違背了長方形的面積是長與寬之積的原則。用戶就不會明白為什么我設置了長是5寬是4得到的答案卻是16 ?? 與前提不符
所以正方形不能代替長方形出現在這個地方。
也就是說正方形不應當看作是長方形的子類。
之前人們討論的正方形長方形的問題的關鍵在哪里?我覺得就在於改動長方形的邊的長度。我們可以這么考慮一下,一個長方形的instance的邊長應該是可變的嗎?我覺得一旦一個長方形的邊長改變之后它就成了另一個長方形了(一個新的instance)。所以長方形類里面不應該有改變其邊長的方法,一個長方形實例各個的邊長應當在new它的時候確定下來,並且它們應當是immutable的。基於這種考慮,我設計的長方形和正方形的類如下所示:
//長方形
public class Rectangle {
private final int width;
private final int height;
public Rectangle(int width, int height) {
this.width = width;
this.height = height;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public int getArea() {
return width*height;
}
}
//正方形
public class Square extends Rectangle{
private final int side;
public Square(int side) {
super(side, side);
this.side = side;
}
public int getSide() {
return side;
}
}
這種繼承關系就既符合現實中的父子關系也遵循LSP。之所以這么設計,我的想法是一個類所具有的方法不應當能夠改變其本質。比如有一個Men類,它可以有eat(),sleep(),work(),makeLovewith(Person p)方法,但是如果你在里面定義denatureToWomen(),denatureToEunuch()就很不恰當了,因為這改變了其本質,導致這個Men的實例不再屬於Men類(至少已經和現實不吻合)了。除非這兩個方法不能改變該實例本質,否則在Men里面定義這兩個方法本身就是有問題的。