天天看點

面向對象的設計原則四 - 裡氏代換原則

動機

當我們設計程式子產品時,我們會建立一些類層次結構,然後我們通過擴充一些類來建立它們的子類。

我們必須確定子類隻是擴充而沒有替換父類的功能,否則當我們在已有程式子產品中使用它們時将會産生不可預料的結果。

裡氏代換原則表明當一個程式子產品使用基類時,基類的引用可以被子類替換而不影響子產品的功能。

裡氏代換原則

基類完全能夠被子類替代而不影響子產品的功能。

執行個體

對于多态來說裡氏代換原則好像是很顯然的事情,例如:

Java代碼

面向對象的設計原則四 - 裡氏代換原則
面向對象的設計原則四 - 裡氏代換原則
面向對象的設計原則四 - 裡氏代換原則

public void drawShape(Shape s) {   

// Code here.   

}  

對于Shape的任何子類來說,drawShape方法都應該能很好的工作。我們必須小心的實作子類以免無意中違反了裡氏代換原則,如果一個函數不滿足裡氏代換原則,那麼它可能必須顯式地引用子類對象,這樣的函數同樣違反了開閉原則,因為當添加新的子類時,必須修改它。

考慮下面的矩形類:// A very nice Rectangle class.

public class Rectangle {

private double width;

private double height;

public Rectangle(double w, double h) {

width = w;

height = h;

}

public double getWidth() {return width;}

public double getHeight() {return height;}

public void setWidth(double w) {width = w;}

public void setHeight(double h) {height = h;}

public double area() {return (width * height);

現在,如果有個正方形呢?顯然正方形是一個矩形,是以我們應該讓正方形繼承矩形類,是這樣嗎?我們看一下!

注意:

正方形不需要同時具有寬和高屬性,但是它還是從矩形繼承了這些屬性。是以,每個正方形都浪費了一點空間,但這不是我們關注的主要問題。

繼承而來的setWidth()和setHeight()方法實際上對于正方形是不合适的,因為正方形的寬和高是相等的。是以我們需要重寫setWidth()和setHeight()方法,這可能暗示着在這兒并不适合使用繼承。

下面是Square類:// A Square class.

public class Square extends Rectangle {

public Square(double s) {super(s, s);}

public void setWidth(double w) {

super.setWidth(w);

super.setHeight(w);

public void setHeight(double h) {

super.setHeight(h);

super.setWidth(h);

一切看上去都很好,但是注意下面的代碼:public class TestRectangle {

// Define a method that takes a Rectangle reference.

public static void testLSP(Rectangle r) {

r.setWidth(4.0);

r.setHeight(5.0);

System.out.println("Width is 4.0 and Height is 5.0" +

", so Area is " + r.area());

if (r.area() == 20.0)

System.out.println("Looking good!/n");

else

System.out.println("Huh?? What kind of rectangle is

this??/n");

public static void main(String args[]) {

// Create a Rectangle and a Square

Rectangle r = new Rectangle(1.0, 1.0);

Square s = new Square(1.0);

// Now call the method above. According to the

// LSP, it should work for either Rectangles or

// Squares. Does it??

testLSP(r);

testLSP(s);

測試程式輸出:Width is 4.0 and Height is 5.0, so Area is 20.0

Looking good!

Width is 4.0 and Height is 5.0, so Area is 25.0

Huh?? What kind of rectangle is this??

看起來我們違反了裡氏代換原則,問題在哪兒?testLSP()方法合理的假設當一個矩形的寬改變時,它的高度不變。當傳遞一個正方形對象時,該方法卻違反了裡氏代換原則。從數學上看,正方形是一個矩形,但是一個正方形對象卻不是矩形對象,因為一個正方形對象的行為和一個矩形對象的行為不一緻。從行為上來說,正方形不是矩形!裡氏代換原則清晰的說明,IS-A關系是對于所有的行為來說的,為了遵循裡氏代換原則,子類的行為必須和用戶端使用的基類的行為一緻。

子類不能比基類具有更多的限制,因為必須在任何可以使用基類的地方使用子類,如果子類比基類有更多的限制,那麼就會出現基類可用,但卻違反了子類限制的情況。

總結

裡氏代換原則是對開閉原則的擴充,它表明我們在建立基類的新的子類時,不應該改變基類的行為。

繼續閱讀