想象一下,您正在构建一个模块来使用不同的 API(Windows GDI、一些智能手机 API、OpenGL 等)绘制形状。有一个典型的层次结构abstract Shape
<---
concrete Circle
,abstract Shape
<---
concrete Rectangle
你必须重新编译和重新部署Circle
,Rectangle
每次你添加一个新的框架,每次在现有的框架中发生一些变化。此类更改甚至可能涉及修改这些类的构造函数,因此您的模块的用户也必须更改他们的代码。
示例:您有一个可以使用的模块的第一个版本,具有以下接口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;
};
当然,您将有许多CircleImpl
s - 每个用于您的模块支持的不同平台(所以CircleImplGDI
,,,CircleImplTk
等CircleImplOpenGL
)。
在实现中,ShapeFactory
您将CircleImpl
适当地创建一个特定的,并且您的模块的客户端不必知道任何关于它的信息。此示例是您提供链接的示例的简化版本。请注意,现在,当使用可能CircleImpl
的 s 之一时,因为Circle
没有实例化抽象类,所以这也应该清除您关于抽象派生类的问题。
这种模式背后的主要思想是有两个抽象层次:Shape
是一个抽象的几何概念,Circle
比Rectangle
更具体,Shape
但在绘制它们的许多技术可能性的背景下,它们仍然是相当抽象的。当您了解上下文时,存在特定形状的具体表示:例如 - 在光栅上绘制或使用矢量图形。
另一个抽象级别使您可以推迟有关代码的更多决定 - 起初我们推迟决定我们拥有哪些形状。然后,Circle
我们Rectangle
推迟决定如何绘制它们。延迟决策为我们提供了解耦、灵活的代码(如“添加 DPI”示例所示)。