轉- 物件導向設計(OOD)中的替換原則

來源:互聯網
上載者:User

http://blog.csdn.net/starlee/article/details/945728

  我們知道,在物件導向語言中,公有繼承是IS-A的關係,也就是說子類是一種基類,就像說轎車是一種汽車一樣。但是,有時候邏輯上正確的公有繼承卻會違反替換原則。物件導向設計中的替換原則是:
    子類必須能夠替換掉它們的基類。
    也就是說,代碼中基類出現的地方都能用子類來替換,就跟汽車能用的地方都能用轎車一樣。但是,如果設計不合理,就會違反這個原則,給開發帶來隱患。
    下面就以一個C++的例子來說明:

class CShape
{
    public:
    virtual ~CShape() {};
    virtual double GetArea() = 0;
};

class CRectangle : public CShape
{
public:
    virtual ~CRectangle() {};
    double GetArea() { return m_dWidth * m_dHeight; };
    void SetWidth(double dWidth) { m_dWidth = dWidth; };
    void SetHeight(double dHeight) { m_dHeight = dHeight; };
    double GetWidth() const { return m_dWidth; };
    double GetHeight() const { return m_dHeight; };

private:
    double m_dWidth;
    double m_dHeight;
};

    現在,我們需要一個正方形類CSquare。我們知道,正方形是一種寬和高相同的長方形,也就是說正方形和長方形符合IS-A的關係。那我們需要的類CSquare是否應該從類CRectangle來繼承呢?
    我們現在先來看看類CSquare從類CRectangle繼承的情況:

class CSquare : public CRectangle
{
public:
    virtual ~CSquare() {};
    void SetWidth(double dWidth) 
    { 
        CRectangle::SetWidth(dWidth); 
        CRectangle::SetHeight(dWidth);
    };
    void SetHeight(double dHeight) 
    { 
        CRectangle::SetHeight(dHeight); 
        CRectangle::SetWidth(dHeight);
    };
};

    類CSquare中對方法SetWidth和方法SetHeight的重載解決了正方形的寬和高相等的問題。但是請考慮下面的代碼:

void FncTest(CRectangle& r)
{
    r.SetWidth(13);
    assert(r.GetArea() == 169);
}

void Test()
{
    CSquare s;
    s.SetWidth(5);

    FncTest(s);
}

    當我們向函數FncTest傳遞一個指向類CSquare對象的引用的時候,這個函數就會出現斷言錯誤,因為類CSquare對象的高度沒有改變。這就違反了替換原則,函數FncTest中的類型為基類CRectangle的參數不能用子類CSquare來替換。錯誤原因很簡單,類CRectangle中的方法SetWidth和方法SetHeight不是虛函數,因此不具備多態性。在這種情況下,我們就必須去修改類CRectangle。下面是修改後的代碼:

class CRectangle : public CShape
{
public:
    virtual ~CRectangle() {};
    double GetArea() { return m_dWidth * m_dHeight; };
    virtual void SetWidth(double dWidth) { m_dWidth = dWidth; };
    virtual void SetHeight(double dHeight) { m_dHeight = dHeight; };
    double GetWidth() const { return m_dWidth; };
    double GetHeight() const { return m_dHeight; };

private:
    double m_dWidth;
    double m_dHeight;
};

class CSquare : public CRectangle
{
public:
    virtual ~CSquare() {};
    virtual void SetWidth(double dWidth) 
    { 
        CRectangle::SetWidth(dWidth); 
        CRectangle::SetHeight(dWidth);
    };
    virtual void SetHeight(double dHeight) 
    { 
        CRectangle::SetHeight(dHeight); 
        CRectangle::SetWidth(dHeight);
    };
};

    這樣,上面提到的問題就解決了,修改也不是很複雜。然而,如果衍生類別的建立會導致我們修改基類,這就意味著設計是有缺陷的。我們先不管是否有設計缺陷,而是先接受上面的修改。現在類CRectangle和類CSquare都能工作,在代碼中也可以接受指向類CRectangle的指標或引用的函數傳遞類CSquare,而類CSquare也可以很好的保持正方形的特性。看起來,這樣的設計已經符合替換原則了。但是,等等,請看下面的函數:

void FncTest(CRectangle& r)
{
    r.SetWidth(5);
    r.SetHeight(6);
    assert(r.GetArea() == 30);
}

    對這個函數來說如果傳遞來的是類CRectangle的對象,則運行正確;如果傳遞來的是類CSque的對象,那就會出現斷言錯誤!仍舊是違反了替換原則!
    上面的例子說明了,即使兩個類是IS-A的關係,也不一定要用公有繼承來實現。
    其實,在物件導向設計(OOD)中,IS-A關係是就行為方式而言的。在本例中,雖然類CRectangle和類CSquare在邏輯上是IS-A關係,但是他們的行為是不同的,因此也就不能讓類CSquare從類CRectangle公有繼承,而是從類CShape公有繼承。
    下面是正確的代碼:

class CShape
{
public:
    virtual ~CShape() {};
    virtual double GetArea() = 0;
};

class CRectangle : public CShape
{
public:
    virtual ~CRectangle() {};
    double GetArea() { return m_dWidth * m_dHeight; };
    void SetWidth(double dWidth) { m_dWidth = dWidth; };
    void SetHeight(double dHeight) { m_dHeight = dHeight; };
    double GetWidth() const { return m_dWidth; };
    double GetHeight() const { return m_dHeight; };

private:
    double m_dWidth;
    double m_dHeight;
};

class CSquare : public CShape
{
public:
    virtual ~CSquare() {};
    double GetArea() { return m_dWidth * m_dWidth; };
    void SetWidth(double dWidth) { m_dWidth = dWidth; };
    double GetWidth() const { return m_dWidth; };

private:
    double m_dWidth;
}; 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.