扩展访客/桥梁模式的两侧

时间:2008-12-09 17:00:48

标签: c# design-patterns visitor bridge

假设我有一个类的层次结构,让我们使用经典的Shape示例:

abstract class Shape
Circle : Shape
Square : Shape

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

abstract class ShapeRenderer
HtmlShapeRenderer : ShapeRenderer
WindowsFormsShapeRenderer : ShapeRenderer

允许这些独立变化传统上涉及使用Bridge模式。允许在不修改Shape类的情况下扩展渲染操作传统上将涉及访问者模式。

然而,这两个都专注于扩展实现方而不是抽象方。说我想添加一个新的Shape,比如说Triangle - 我希望能够支持渲染Triangle。由于Visitor和Bridge模式都依赖于将抽象层次“扁平化”为一组方法,例如:

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

扩展Shape层次结构的唯一方法是修改基类ShapeRenderer类的代码,这是一个重大变化。

Jon,澄清一下:使用Bridge或Visitor允许客户端提供替代的渲染实现,但要求他们了解所有潜在的Shapes。我希望能够做的是允许客户 能够扩展Shape类和要求它们为他们提供渲染实现新课。这样,现有代码可以使用任何类型的Shape,而无需担心渲染它们的细节。

C#中可以使用这种问题的常见解决方案吗?

4 个答案:

答案 0 :(得分:2)

我认为应该是一个突破性的变化。如果添加形状,现有的渲染器显然无法应对 - 它们需要更改。

您可以更改ShapeRenderer以将RenderTriangle()添加为虚拟(非抽象)方法,该方法仅记录无法正确渲染的事实,然后一次修复一个渲染器,但从根本上说,您是没有更多代码就无法呈现新类型。

你真的希望实现什么样的非破坏性改变?

答案 1 :(得分:2)

strategy pattern怎么样?策略是RenderEngine实现的引用。当您想要添加新形状时,您可以创建渲染引擎的新实现,该实现了解新的Shape实现并实现相应的渲染功能。您向Shape添加一个虚函数,它作为辅助函数来选择正确的形状渲染函数 - 即Circle对象调用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();

答案 2 :(得分:1)

设计接口而不是实现。

嘿 - 我今天两次使用相同的答案(我想Renderer是一个实现是有争议的)...

我不确定我会选择ShapeRenderer类。那么由形状类实现的IRenderHTML,IRenderWindows呢?

您可以使用“形状”和“渲染”获得可扩展性。

我认为,将自己的圈子传递给实用程序类进行渲染可能会更好。您可以通过让形状自己进行渲染来轻松添加新形状和新渲染。

答案 3 :(得分:1)

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

这样做意味着添加新形状只需要更改配置存储,以便工厂知道要映射哪些渲染器以及包含具体实现的新程序集。