DECORATOR模式--《敏捷软件开发》读书笔记(二)

来源:互联网 发布:大数据的前沿技术论文 编辑:程序博客网 时间:2024/05/16 01:03

    比如现在我们有如下的几个类:

class CGShape
{
public:
    
~CGShape();
    
virtual void Draw() = 0;
};

class CGRectangle : public CGShape
{
public:
    
void Draw();
};

class CGSquare : public CGShape
{
public:
    
void Draw();
};

class CGCircle : public CGShape
{
public:
    
void Draw();
};

    如果现在有些代码的用户有一个特殊的需要,就是要在绘制出图形的时候同时把图形的中心点也绘制出来,那我们该怎么办呢?
    很显然,不能直接给各个类的Draw方法中加上绘制中心点的代码,因为并不是所有的用户都需要这个功能。
    那么我们可以给基类CGShape增添一个bool类型的成员变量m_bDrawCenter,并添加一个设置该变量值的公有方法SetDrawCenter,再在各个子类的Draw方法里面根据变量m_bDrawCenter的值来判断是否绘制中心点。需要绘制中心点的用户在调用Draw方法之前先调用SetDrawCenter方法把m_bDrawCenter设置为true。
    还有一个方法,就是分别以类CGRectangle、CGSquare和CGCircle为基类继承,在各个子类中重载Draw方法,在该方法里面添加绘制中心点的代码。
    上面两种方法都可行,可是我们来仔细分析一下这两种方法的缺陷:
    方法一,每个类中的Draw方法中都要添加判断变量m_bDrawCenter的值的代码和绘制中心点的代码,这些代码在每个类中都是重复的,添加的工作也是非常枯燥无味。如果说类很多的话,那么做这个工作的程序员会崩溃的。最重要的一点是,如果上面那些类都是包含在库文件中,没有源代码,那么这个方法就根本行不通。
    方法二,看起来似乎很符合面向对象设计(OOD),也不需要上面那些类的源代码。但是,这样会产生大量子类,并增加了类的层次结构。而且继承下来的每个子类也都是修改Draw方法,而且添加的绘制中心点的代码也都是重复的。
    为了避免上面所说的各种缺陷,我们可以使用DECORATOR模式。看下面这个类:

class CGDrawShapeWithCenter : public CGShape
{
public:
    CGDrawShapeWithCenter(CGShape 
*pShape);
    
void Draw();

private:
    CGShape 
*m_pShape;
};

CGDrawShapeWithCenter::CGDrawShapeWithCenter(CGShape 
*pShape)
{
    
// 得到指向具体图形的指针
    m_pShape = pShape;
}

void CGDrawShapeWithCenter::Draw()
{
    m_pShape
->Draw();
    
// 在这里添加绘制中心点的代码
}

    这个类使用起来非常简单,下面这个函数就是绘制一个圆以及圆心:

void DrawCircleWithCenter()
{
    CGCircle c
ircle;
    CGDrawShapeWithCenter shape(&circle);
    shape.Draw();
}

    在类CGDrawShapeWithCenter中,我们通过一个指向CGShape的指针获得了要绘制的图形的控制权,用户在调用Draw方法时并没有觉得有什么不同。我们在即不会修改任何类,也不会产生大量子类,更不会有大量重复代码的情况下,满足了特殊用户的需求。
    当然我上面举的这个例子非常简单,但是足以说明DECORATOR模式的优点。需要注意的一点就是,类CGDrawShapeWithCenter必须实现所有类CGShape的接口,在不需要修改的接口实现中只要调用指向CGShape的指针m_pShape相应的方法就行了。
    现在,有人也许会问:为什么类CGDrawShapeWithCenter要从类CGShape继承?从上面的代码上来看,单独写一个类CGDrawShapeWithCenter也可以实现所有的功能。但是,请看下面这个函数:

void DrawShape(CGShape *pShape)
{
    pShape
->Draw();
}

    这个函数是用户写来绘制图形的。如果类CGDrawShapeWithCenter不是从类CGShape继承,那么要在绘制图形的同时把图形的中心也绘制出来,就必须修改这个函数:

void DrawShape(CGDrawShapeWithCenter *pShape)
{
    pShape
->Draw();
}

    而如果类CGDrawShapeWithCenter是从类CGShape继承来的,用户就不用修改上面的函数。因为利用类的多态性,把一个类CGDrawShapeWithCenter对象的指针传给函数就行了。比如下面这样:

CGCircle circle;
CGDrawShapeWithCenter shape(
&circle);
DrawShape(
&shape);

    也就是说,所有用CGShape的地方都能用CGDrawShapeWithCenter,从而发挥C++类的多态性的强大功能。在这种情况下,用户代码的修改量也可以降到最小。而这样做最大的好处是decorator类CGDrawShapeWithCenter还能被其他decorator类来修饰。其实,DECORATOR模式最精妙的地方就是decorator类是从要修饰的类的基类继承,而且有一个指向被修饰类的指针的成员。

原创粉丝点击