Расширение обеих сторон шаблона посетитель / мост - PullRequest
3 голосов
/ 09 декабря 2008

Скажем, у меня есть иерархия классов, давайте использовать классические Shape примеры:

abstract class Shape
Circle : Shape
Square : Shape

У меня есть вторая иерархия классов визуализации, которые по-разному обрабатывают рендеринг фигур:

abstract class ShapeRenderer
HtmlShapeRenderer : ShapeRenderer
WindowsFormsShapeRenderer : ShapeRenderer

Разрешение их варьирования независимо обычно предполагает использование шаблона Мост. Разрешение действия рендеринга без изменения классов Shape традиционно включает шаблон Visitor.

Однако оба они сосредоточены исключительно на расширении стороны реализации, а не стороны абстракции. Скажем, я хотел добавить новый Shape, скажем Triangle - я хочу иметь возможность поддерживать рендеринг Triangle. Поскольку и шаблон «Посетитель» и «Мост» полагаются на «уплощение» иерархии абстракций в набор методов, например ::

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

Единственный способ расширить иерархию Shape - это изменить код базового класса ShapeRenderer, что является серьезным изменением.

Джон, чтобы уточнить: Использование Bridge или Visitor позволяет клиентам предоставлять альтернативные реализации рендеринга, но требует, чтобы они знали обо всех потенциальных Shapes. То, что я хотел бы сделать, это позволить клиентам также иметь возможность расширять класс Shape, а требовать от них для обеспечения реализации рендеринга для их нового класса. Таким образом, существующий код может работать с любым типом Shape, не беспокоясь об особенностях их рендеринга.

Есть ли общее решение проблемы такого рода, которое можно использовать в C #?

Ответы [ 4 ]

2 голосов
/ 10 декабря 2008

Как насчет стратегии ? Где стратегия является ссылкой на реализацию 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 голосов
/ 09 декабря 2008

Я думаю, что должно быть серьезным изменением. Если вы добавите фигуру, существующие средства визуализации, очевидно, не смогут справиться - их нужно будет изменить.

Вы можете изменить ShapeRenderer, чтобы добавить RenderTriangle () как виртуальный (неабстрактный) метод, который просто регистрирует тот факт, что он не может соответствующим образом рендериться, а затем исправляет рендереры по одному, но по сути вы не удастся отобразить новый тип без дополнительного кода.

Каких нерушимых изменений вы действительно надеетесь достичь?

1 голос
/ 09 декабря 2008

Мое решение здесь почти наверняка состоит в том, чтобы использовать абстрактную фабрику, и в этом случае я бы загружал словарь ShapeRenderers с ключом по типу, где тип - это подкласс Shape, и пусть фабрика предоставляет ShapeRenderer, требуемый для каждого. Форма (и, возможно, платформа, например, Window, Web, iPhone).

Выполнение этого означает, что добавление новой фигуры потребует только изменения в хранилище конфигурации, поэтому фабрика знает, какие средства визуализации отображать с какими фигурами, и новую сборку, содержащую конкретные реализации.

1 голос
/ 09 декабря 2008

Дизайн интерфейса не является реализацией.

Эй, сегодня я получаю один и тот же ответ дважды (думаю, что можно утверждать, что Renderer - это реализация) ...

Я не уверен, что пойду с классом ShapeRenderer. А как насчет IRenderHTML, IRenderWindows, которые реализуются классами фигур?

Вы получаете расширяемость как с помощью Shapes, так и с Renderings.

Я думаю, что было бы лучше, если бы О.О. сказал: «Эй, круг иди визуализируй себя», чем передать круг в служебный класс для рендеринга. Вы можете легко добавлять новые фигуры и новые визуализации, позволяя фигурам выполнять визуализацию самостоятельно.

...