【问题标题】:Bridge pattern example from "Designed Patterns Explained"“Designed Patterns Explained”中的桥接模式示例
【发布时间】:2013-01-06 16:25:24
【问题描述】:

我正在研究“设计模式解释”中的桥接模式示例。 我正在查看的示例是示例 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 在继承类中可以是纯虚拟的?

【问题讨论】:

  • 那么,为什么不应该它可以是虚拟的?
  • 你应该改写你的标题以匹配你的实际问题;这与桥接模式无关..
  • stijn - 好点我也许应该包含所有代码。但是这个例子是为了实现桥接模式,作者使用桥接模式将抽象(形状类)与调用drawCircle提供“桥接”的实现(绘图类)分离。

标签: c++ oop design-patterns bridge


【解决方案1】:

假设您正在构建一个模块以使用不同的 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 - 每个都适用于您的模块支持的不同平台(例如,CircleImplGDICircleImplTkCircleImplOpenGL 等)。

ShapeFactory 的实现中,您将适当地创建一个特定的CircleImpl,并且您的模块的客户端不必对此有所了解。此示例是您提供链接的示例的简化版本。请注意,现在,当可能的 CircleImpls 之一用作 Circle 时,没有抽象类被实例化,因此这也应该解决您关于抽象派生类的问题。

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

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

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • 是的,我对这个例子的问题不是派生类是纯虚拟的,而是实现是在基类中定义的。另一个问题是基类和派生类都是抽象的,所以很明显有一个错误......
    • @emza0114 您应该使用此信息更新您的问题。为什么父类和子类都是抽象的会是错误的?
    • 正如你所说,我可以创建什么实例?
    • @emza0114 您链接到的代码示例缺少很多代码。它编译是因为它从不创建实际的实例。
    猜你喜欢
    • 2010-12-20
    • 2013-05-31
    • 1970-01-01
    • 2017-07-14
    • 2019-03-03
    • 2011-01-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多