1

我正在研究“Designed Patterns Explained”中的桥接模式示例。我正在查看的示例是示例 10.3,可以在以下位置找到

http://www.netobjectives.com/resources/books/design-patterns-explained/cpp-code-examples/chapter10#10-3

我的具体困惑在于 Shape 类及其派生类。

#pragma once
#include "Drawing.h"

class Shape
{
public:
    Shape(Drawing *aDrawing);
    virtual void draw()= 0;

protected:
    Drawing *myDrawing;
    void drawLine( double, double, double, double);
    void drawCircle( double, double, double);

public:
    ~Shape(void);
};

在 Circle 类中,我们有

#pragma once
#include "Shape.h"

class Circle : public Shape
{
public:
    Circle(Drawing*, double, double, double);
    virtual void draw();
    virtual void drawCircle(double, double, double)=0;

public:
    ~Circle(void);
protected:
    double _x, _y, _r;
};

所以我的问题是:drawCircle既然方法实际上是在基类中实现的,为什么在继承类中可以是纯虚拟的?

4

2 回答 2

4

想象一下,您正在构建一个模块来使用不同的 API(Windows GDI、一些智能手机 API、OpenGL 等)绘制形状。有一个典型的层次结构abstract Shape <--- concrete Circleabstract Shape <--- concrete Rectangle你必须重新编译和重新部署CircleRectangle每次你添加一个新的框架,每次在现有的框架中发生一些变化。此类更改甚至可能涉及修改这些类的构造函数,因此您的模块的用户也必须更改他们的代码。

示例:您有一个可以使用的模块的第一个版本,具有以下接口Circle

class Circle : public Shape
{
  public:
    Circle(int x, int y, int radius);

    void draw(...);
};

然后,恰好其中一个平台的优化原因迫使您提前知道DPI当前平台的分辨率(在实际绘制圆圈之前)。因此,您将不得不更改构造函数:

class Circle : public Shape
{
  public:
    Circle(int x, int y, int radius, int dpi);

    void draw(...);
};

并且您的代码的客户将不得不重新编译他们的应用程序。当然会有一些技巧可以避免这种情况(例如引入CircleWithDpi),但它们会导致高度耦合且难以维护的代码。如果你使用桥接模式,你可以让你的清晰设计保持原样,并且仍然表达你的领域(一般来说,“圆”的概念不应该知道任何关于“dpi 分辨率”的东西)。

所以有:

class Circle : public Shape
{
  public:
    Circle(int x, int y, int radius);

    virtual void draw(...) = 0;
};

class CircleImpl : public Circle
{
  public:
    CircleImpl(int x, int y, int radius, int dpi);
    //perform some calculations before drawing for optimization

    void draw(...);
    //draw using appropriate API
};

class ShapeFactory
{
  public:
    virtual Circle* CreateCircle(int x, int y, int radius) = 0;
};

当然,您将有许多CircleImpls - 每个用于您的模块支持的不同平台(所以CircleImplGDI,,,CircleImplTkCircleImplOpenGL)。

在实现中,ShapeFactory您将CircleImpl适当地创建一个特定的,并且您的模块的客户端不必知道任何关于它的信息。此示例是您提供链接的示例的简化版本。请注意,现在,当使用可能CircleImpl的 s 之一时,因为Circle没有实例化抽象类,所以这也应该清除您关于抽象派生类的问题。

这种模式背后的主要思想是有两个抽象层次:Shape是一个抽象的几何概念,CircleRectangle更具体,Shape但在绘制它们的许多技术可能性的背景下,它们仍然是相当抽象的。当您了解上下文时,存在特定形状的具体表示:例如 - 在光栅上绘制或使用矢量图形。

另一个抽象级别使您可以推迟有关代码的更多决定 - 起初我们推迟决定我们拥有哪些形状。然后,Circle我们Rectangle推迟决定如何绘制它们。延迟决策为我们提供了解耦、灵活的代码(如“添加 DPI”示例所示)。

于 2013-09-15T10:27:17.067 回答
1

任何类中都允许使用纯虚方法,只要您不尝试创建该类的实例。

于 2013-01-23T08:48:43.593 回答