English 中文(简体)
扩展访客/桥接模式的两侧
原标题:
  • 时间:2008-12-09 17:00:48
  •  标签:

假设我有一组类别的层级结构,让我们使用经典的形状(Shape)示例:

abstract class Shape
Circle : Shape
Square : Shape

我有第二个渲染器类层次结构,它以不同的方式处理形状的渲染:

abstract class ShapeRenderer
HtmlShapeRenderer : ShapeRenderer
WindowsFormsShapeRenderer : ShapeRenderer

允许它们独立变化通常需要使用桥接模式。允许渲染操作在不修改Shape类的情况下扩展通常需要使用访问者模式。

然而,这两种方式都仅专注于扩展实现方面,而非抽象方面。比如说,我想添加一个新的Shape,比如Triangle,我也希望能够支持渲染Triangle。由于访问者模式和桥接模式都依赖于将抽象层级压缩为一组方法,例如:

public abstract class ShapeRenderer
{
     public abstract void RenderCircle(Circle c);
     public abstract void RenderSquare(Square s);
}

唯一扩展Shape层次结构的方法是修改基础ShapeRenderer类的代码,这是一种破坏性的变更。

乔恩,澄清一下:使用桥接器或访问者允许客户端提供替代渲染实现,但需要他们了解所有潜在的形状。我想做的是允许客户端可以扩展Shape类并要求他们为他们的新类提供渲染实现。这样,现有的代码可以使用任何类型的Shape,而不必担心渲染的具体情况。

在C#中,这种问题有通用的解决方案吗?

问题回答

我认为这应该是一个重大变更。如果您添加形状,那么现有的渲染器显然无法应对-它们需要被更改。

你可以将ShapeRenderer更改为添加RenderTriangle()作为虚拟(非抽象)方法,该方法只记录无法适当呈现的事实,然后逐个修复呈现器,但基本上你没有办法在没有更多代码的情况下呈现新类型。

你希望实现哪种不破坏性变化?

策略模式怎么样?其中策略是对RenderEngine实现的引用。当您想要添加新形状时,创建新的渲染引擎实现,该实现了解新的Shape实现并实现相应的渲染函数。您需要向Shape添加一个虚函数,作为选择正确形状渲染函数的帮助函数 - 即圆形对象调用renderCircle()函数等。

在C++中,可能会看起来像这样:

class Triangle : public Shape
{
  public:
      Triangle( const RenderEngine& whichRenderEngine );
      void render( void ) { renderStrategy->renderTriangle( *this );

  private:
      RenderEngine* renderStrategy;
};

class TriangleRender : HTMLShapeRender
{
   public:
      // if inheriting from concrete class, all other rendering functions 
      // already exist... otherwise re-implement them here.

      void renderTriangle( const Triangle& t ) { /* impl */ }
};

HTMLRenderer r; // doesn t know about Triangles.
Circle c( &r );
c.render();

Square s( &r );
s.render();

// Now we add Triangle
TriangleRenderer tr;
Triangle t( &tr );
t.render();

Square s2( &tr );  // tr still knows how to render squares... 
s2.render();

按照接口设计,而非实现。

嗨 - 我今天可以两次使用相同的答案(我猜Renderer是否为实现是有争议的)...

我不确定是否应该使用ShapeRenderer类。那么实现形状类的IRenderHTML和IRenderWindows呢?

您不仅可以通过渲染进行可扩展性,还可以通过形状进行可扩展性。

我认为使用OO理念,让圆形自行渲染会更好,而不是将圆形传递给一个实用类进行渲染。通过让形状自己进行渲染,您可以很容易地添加新形状和新渲染方式。

我的解决方案几乎肯定是使用抽象工厂,这种情况下,我会加载一个按类型键入的 ShapeRenderers 字典,其中类型是 Shape 的子类,并让工厂提供每个 Shape 所需的 ShapeRenderer(以及可能的平台,如 Window、Web、iPhone)。

用这种方式做就意味着添加新的形状只需要更改配置存储,这样工厂就知道将哪些渲染器与哪些形状映射,以及包含具体实现的新装配件。





相关问题
热门标签