設計模式之 開放封閉原則OCP C++樣本

來源:互聯網
上載者:User

原文是C++ VIEW第二期的一篇譯文,這裡做個總結,便於查閱。

開放封閉原則

系統在添加新的需求的時候能夠儘可能做到,只是添加新的代碼 open for extension,而不需要修改原有模組代碼 closed for modification

 

通過提取基類的方法,client 調用server 抽象基類abstract server的抽象介面,從而更換不同sever的時候,client的調用server的代碼都不需要改動,介面不變,

只是server內容變化。

 

例子,

一個繪製函數,要求能夠針對輸入的不同對象,調用不同的繪製函數,如能夠繪製矩形,圓形,適當調用矩形繪製函數,圓形繪製函數。

1.用c語言實現

這個例子其實給出了,c語言類比c++類繼承的方法。利用指標的強制轉換,因為指標僅僅是地址可以指向任何對象,利用指標強制轉換,告訴編譯器具體按什麼對象處理指標所指。

Listing 1
/*Procedural Solution to the Square/Circle Problem*/

enum ShapeType {circle, square};
struct Shape
{
    ShapeType itsType;
};
struct Circle
{
    ShapeType itsType;
    double itsRadius;
    Point itsCenter;
};
struct Square
{
    ShapeType itsType;
    double itsSide;
    Point itsTopLeft;
};
//
// 下面兩個函數的實現定義在別處
//
void DrawSquare(struct Square*)
void DrawCircle(struct Circle*);
typedef struct Shape *ShapePointer;
void DrawAllShapes(ShapePointer list[], int n)
{
    int i;
    for (i=0; i<n; i++)
   {
        struct Shape* s = list[i];
        switch (s->itsType)
        {
          case square:
              DrawSquare((struct Square*)s);
              break;
         case circle:
             DrawCircle((struct Circle*)s);
             break;
        }
     }
}

 

上面的代碼不符合open close法則,因為新加入其它的shape如橢圓, DrawAllShapes函數就需要變化。

2. C++的實現

Listing 2
/*OOD solution to Square/Circle problem.*/
class Shape
{
    public:
        virtual void Draw() const = 0;
};
class Square : public Shape
{
    public:
        virtual void Draw() const;
};
class Circle : public Shape
{
    public:
        virtual void Draw() const;
};
void DrawAllShapes(Set<Shape*>& list)
{
    for (Iterator<Shape*>i(list); i; i++)
        (*i)->Draw();
}

 

和上面C語言實現代碼對比,顯然符合open close 法則,加入新的shape, DrawAllShapes函數可保持不變,只是添加新的shape內容。

 

但是事實上如果有新的需求變化,DrawAllShapes也無法做到完全不變,任何模組只能是相對封閉,無法完全封閉

例如我們有新的需求,要求繪製圖形列表的時候,一種形狀的圖形要在另一種圖形前面繪製

解決方案,加入 順序抽象類別

Listing 3
/*Shape with ordering methods.*/
class Shape
{
    public:
        virtual void Draw() const = 0;

        virtual bool Precedes(const Shape&) const = 0;
        bool operator<(const Shape& s) {return Precedes(s);}
};

 

Listing 4
/*DrawAllShapes with Ordering*/
void DrawAllShapes(Set<Shape*>& list)
{
// copy elements into OrderedSet and then sort.
    OrderedSet<Shape*> orderedList = list;
    orderedList.Sort();
    for (Iterator<Shape*> i(orderedList); i; i++)
        (*i)->Draw();
}

Listing 5
/*Ordering a Circle*/
bool Circle::Precedes(const Shape& s) const
{
    if (dynamic_cast<Square*>(s))
        return true;
    else
        return false;
}

 

這裡使用的Precedes函數,如果新加入shape需要改變,怎麼樣才能做到更好呢?

使用資料驅動獲得封閉性,利用預先寫好的table,我們將各個圖形的優先順序寫入table,那麼新加入shape只需要更新table加入新的shape。

Listing 6
/*Table driven type ordering mechanism*/
#include <typeinfo.h>
#include <string.h>
enum {false, true};
typedef int bool;
class Shape
{
public:
    virtual void Draw() const = 0;
    virtual bool Precedes(const Shape&) const;
    bool operator<(const Shape& s) const
        {return Precedes(s);}
private:
    static char* typeOrderTable[];
};

 

/*
譯者註:由於typeinfo.name沒有標準,因此最好直接用typeinfo作為表中的元素類型,而不是用類名字串。
*/
char* Shape::typeOrderTable[] =
{
    “Circle”,
    “Square”,
    0
};
// This function searches a table for the class names.
// The table defines the order in which the
// shapes are to be drawn. Shapes that are not
// found always precede shapes that are found.
//
bool Shape::Precedes(const Shape& s) const
{

    const char* thisType = typeid(*this).name();
    const char* argType = typeid(s).name();
    bool done = false;
    int thisOrd = -1;
    int argOrd = -1;
    for (int i=0; !done; i++)
    {
        const char* tableEntry = typeOrderTable[i];
        if (tableEntry != 0)
        {
            if (strcmp(tableEntry, thisType) == 0)
                thisOrd = i;
            if (strcmp(tableEntry, argType) == 0)
                argOrd = i;
            if ((argOrd > 0) && (thisOrd > 0))
                done = true;
        }
        else // table entry == 0
            done = true;
    }
    return thisOrd < argOrd;
}

 

進一步擴充封閉性
故事還沒有結束。我們已經設法使得Shape類層次和DrawShapes函數對於依賴於圖形類型的畫出順序是封閉的。然而,如果畫出順序與圖形類型無關,那麼Shape衍生類別並不對這種順序的變化封閉。我們似乎需要根據一個更加高層次的結構來決定畫出各個shape的順序。關於這個問題的深入徹底探討已經超過了本文的範圍;然而有興趣的讀者可能會考慮定義一個OrderedObject的抽象類別,並從Shape類和OrderedObject類派生一個新的抽象類別OrderedShape。

 

所有成員變數都應該是私人的

永遠不要用全域變數

然而,有些情況下全域變數的方便性是很重要的。全域變數cout和cin就是例子。在這種情況下,如果沒有破環開放―封閉(open-closed)原則,那麼犧牲風格來獲得這種方便性是值得的

RTTI是危險的

根據一般的經驗,如果使用RTTI不會破壞開放―封閉(open-closed)原則,那麼就是安全的

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.