2008-12-09 6 views
3

Say У меня есть иерархия классов, давайте использовать классические Shape примеры:Расширение обе стороны шаблона Visitor/Bridge

abstract class Shape
Circle : Shape
Square : Shape

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

abstract class ShapeRenderer
HtmlShapeRenderer : ShapeRenderer
WindowsFormsShapeRenderer : ShapeRenderer

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

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

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

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

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

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

ответ

2

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

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

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

+0

Но мы все хотим волшебства! [абсолютная инкапсуляция и т. д. :)] – mlvljr 2010-03-20 21:10:27

1

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

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

Я не уверен, я бы с классом ShapeRenderer. Что относительно IRenderHTML, IRenderWindows, которые реализуются классами формы?

Вы получаете расширяемость с помощью фигур, а также с помощью рендеринга.

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

+0

Мы это рассмотрели, но я не уверен, что сама форма должна указать, на каких платформах она может быть отображена. Недостатком здесь является то, что добавление другой платформы рендеринга требует возврата и реализации нового интерфейса визуализации для всех фигур. – 2008-12-09 18:40:53

+0

Если (в маловероятном случае) вам нужно внедрить новую платформу, вы все равно должны реализовать рендер. Полиморфный рендеринг для любой платформы может снизить влияние на отображаемый контент. Добавление дополнительного интерфейса к существующему классу должно быть довольно низким. – mson 2008-12-09 19:09:30

1

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

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

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();