4

クラスの階層があるとします。古典的な例を使用しましょうShape

abstract class Shape
Circle : Shape
Square : Shape

さまざまな方法で形状のレンダリングを処理するレンダラー クラスの 2 番目の階層があります。

abstract class ShapeRenderer
HtmlShapeRenderer : ShapeRenderer
WindowsFormsShapeRenderer : ShapeRenderer

これらを個別に変更できるようにするには、従来、ブリッジ パターンを使用する必要がありました。クラスを変更せずにレンダリング アクションを拡張できるようにするには、Shape従来、Visitor パターンが必要でした。

ただし、これらは両方とも、抽象化側ではなく、実装側の拡張のみに焦点を当てています。新しい を追加したいとします。Shapeたとえば、TriangleのレンダリングもサポートできるようにしたいとしTriangleます。Visitor パターンと Bridge パターンの両方が、抽象化階層を一連のメソッドに「フラット化」することに依存しているため、次のようになります。

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

階層を拡張する唯一の方法Shapeは、基本クラスのコードを変更するShapeRendererことです。これは重大な変更です。

ジョン、明確にするために:ブリッジまたはビジターを使用すると、クライアントは代替のレンダリング実装を提供できますが、潜在的なすべてのシェイプについて知る必要があります。私ができるようにしたいのは、クライアントがクラス拡張できるようにし、新しいクラスのレンダリング実装を提供するようにクライアントに要求することです。このように、既存のコードは、レンダリングの詳細を気にすることなく、あらゆるタイプの で動作します。ShapeShape

C# で使用できるこの種の問題に対する一般的な解決策はありますか?

4

4 に答える 4

2

戦略パターンはどうですか?戦略がRenderEngine実装への参照である場合。新しいシェイプを追加する場合は、新しいシェイプの実装を認識し、対応するレンダリング関数を実装するレンダリングエンジンの新しい実装を作成します。正しい形状レンダリング関数を選択するためのヘルパー関数として機能する仮想関数を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();
于 2008-12-10T03:50:34.450 に答える
2

破壊的な変更であるべきだと思います。形状を追加すると、既存のレンダラーでは明らかに対応できなくなります。変更する必要があります。

ShapeRenderer を変更して RenderTriangle() を仮想 (非抽象) メソッドとして追加し、適切にレンダリングできないという事実をログに記録し、レンダラーを 1 つずつ修正することもできますが、基本的にはそうするつもりはありません。コードを追加せずに新しい型をレンダリングできます。

あなたが本当に達成したいのは、どのような破壊的でない変化ですか?

于 2008-12-09T17:17:45.600 に答える
1

実装ではなくインターフェースへの設計。

ねえ-今日、同じ答えを2回使用するようになりました(Rendererが実装であることは議論の余地があると思います)...

ShapeRenderer クラスを使用するかどうかはわかりません。形状クラスによって実装される IRenderHTML や IRenderWindows はどうですか?

Shapes と Renderings の拡張性が得られます。

円をユーティリティ クラスに渡してレンダリングするよりも、自分で円をレンダリングすると言うほうがよい OO かもしれないと思います。シェイプ自体にレンダリングを行わせることで、新しいシェイプと新しいレンダリングを簡単に追加できます。

于 2008-12-09T17:22:06.933 に答える
1

ここでの私の解決策は、ほぼ間違いなく、Abstract Factory を使用することです。その場合、type は Shape のサブクラスであり、ファクトリが各 Shape に必要な ShapeRenderer を提供できるように、タイプごとにキーが付けられた ShapeRenderer の辞書をロードします (およびおそらくプラットフォーム (例: Window、Web、iPhone)。

このように行うと、新しい形状を追加するために構成ストアを変更するだけで済み、ファクトリはどのレンダラーをどの形状にマップするかを認識し、具体的な実装を含む新しいアセンブリが必要になります。

于 2008-12-09T17:58:57.747 に答える