zoukankan      html  css  js  c++  java
  • <总结> 设计模式之 开放封闭原则OCP C++示例

    原文是C++ VIEW第二期的一篇译文,这里做个总结,便于查阅。

    开放封闭原则

    系统在添加新的需求的时候能够尽可能做到,只是添加新的代码 open for extension,而不需要修改原有模块代码 closed for modification

    image

    image

    通过提取基类的方法,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)原则,那么就是安全的

  • 相关阅读:
    【luogu P7418】Counting Graphs P(DP)(思维)(容斥)
    【luogu P7417】Minimizing Edges P(贪心)(思维)
    多边形序列(组合数)(高精)(NTT)
    【luogu P3803】【模板】多项式乘法(NTT)
    【luogu P1919】【模板】A*B Problem升级版(FFT快速傅里叶)
    【luogu P6139】【模板】广义后缀自动机(广义 SAM)
    【luogu P7529】Permutation G(几何)(数学)(DP)
    【luogu P5787】graph / 二分图 /【模板】线段树分治(扩展域并查集)(线段树分治)
    同桌的你(环套树)(DP)
    石子游戏(博弈论)(Spaly)
  • 原文地址:https://www.cnblogs.com/rocketfan/p/1566907.html
Copyright © 2011-2022 走看看