1

私はアプリケーションアーキテクチャに次の問題があり、それを解決したいと思っています(多くのテキストで申し訳ありません)。

私はゲームエンジンのプロトタイプを作成していて、基本抽象クラスを持っていAbstractRenderer ます(C ++構文を使用しますが、それでも問題は一般的です)。

このレンダラーの派生実装がいくつかあると仮定します。たとえばDirectxRenderer、とOpenglRenderer

さて、これらのレンダラーの1つ(DirectXベースに固執しましょう)だけが、IDirect3D9Device* m_device;明らかにこの時点ですべてが正常であるというメンバーを持っているとしましょう-m_device内部で使用DirectxRendererされ、抽象スーパークラスで公開されるべきではありませんAbstractRenderer

たとえば、抽象的なレンダリングインターフェイスも追加しますIRenderable。それは単に1つの純粋な仮想メソッドを意味しますvirtual void Render(AbstractRenderer* renderer) const = 0;


そして、これはいくつかの問題が始まる場所です。あるシーンをモデリングしていると仮定すると、このシーンにはおそらくいくつかの幾何学的オブジェクトが含まれます。

抽象スーパークラスAbstractGeometricalObjectと派生DirectXベースの実装を作成しますDirectxGeometricalObject。2つ目は、DirectX固有の頂点およびインデックスバッファーへのポインターを格納する役割を果たします。

今-問題。

AbstractGeometricalObject論理的にレンダリング可能IRenderableであるため、明らかにインターフェイスを派生させる必要があります。

DirectxGeometricalObjectがから派生した場合、最初のものにはメソッドが含まれているAbstractGeometricalObjectはずであり、そのようなものはいくつかの問題を引き起こします。virtual void Render(AbstractRenderer* renderer) const { ... }Abstract...

より良い説明については、コードを参照してください。

そして今のところ、私のクラスは次のように見えます。

class AbstractGeometricalObject : public IRenderable {
    virtual void Render(AbstractRenderer* renderer) const { ... }
};

class DirectxGeometricalObject : public AbstractGeometricalObject {

    virtual void Render(AbstractRenderer* renderer) const {

    // I think it's ok to assume that in 99 / 100 cases the renderer
    // would be a valid DirectxRenderer object

    // Assume that rendering a DirectxGeometricalObject requires
    // the renderer to be a DirectxRenderer, but not an AbstractRenderer
    // (it could utilize some DX-specific settings, class members, etc

    // This means that I would have to ***downcast*** here and this seems really
    // bad to me, because it means that this architecture sucks

    renderer = dynamic_cast<DirectxRenderer*>(renderer);

    // Use the DirectX capabilities, that's can't be taken out
    // to the AbstractRenderer superclass
    renderer.DirectxSpecificFoo(...);
}

おそらく心配しすぎていることはわかっていますが、このような単純なケースでのこのダウンキャストは、アプリケーションが大きくなると、多くのダウンキャストを強制される可能性があることを意味します。

絶対に避けたいので、デザインの面でもっと良いアドバイスをお願いします/私の間違いを指摘してください。

ありがとうございました

4

6 に答える 6

3

これは、テンプレートパターン(C ++テンプレートと混同しないでください)が役立つ状況である可能性があります。抽象クラスのパブリックRenderは非仮想である必要がありますが、プライベート仮想関数(DoRenderなど)を呼び出す必要があります。次に、派生クラスで、代わりにDoRenderをオーバーライドします。

これは、プライベート仮想関数でのテンプレートパターンの使用について詳しく説明している記事です。

編集:

私が意味することの例をまとめ始めました、そしてそれは実際にはアーキテクチャにもっと広い欠陥があるようです。AbstractRendererの使用は、各ジオメトリックオブジェクトに特定のレンダラータイプを密接に認識させるため、やや取るに足らないものです。

レンダラーは、レンダラブルのパブリックメソッドを処理できる必要があります。または、レンダラブルは、レンダラのパブリックメソッドを処理できる必要があります。または、このような密接な関係が本当に必要な場合は、コンクリートレンダラーにレンダリング可能なファクトリを与えることができます。他にもうまく合うパターンがあると思います。

于 2010-07-14T20:34:39.763 に答える
2

あなたのコードが何を達成したいのかわかりません。RenderableオブジェクトをDirectXRenderablesおよびOpenGLRenderablesに派生させてから、Rendererから派生したものでOpenGLまたはDirectX機能を提供します。特定のものは、いわば別の特定のものを使用します。pure virtual一般的なレンダリング関数を識別し、それらを抽象レンダラーのメンバーにして、およびに実装するDirectXRenderer方がはるかに合理的と思われますOpenGLRenderer。次に、aIRenderableはメンバー関数を大まかに次のように描画します。

void draw(const AbstractRenderer& r) {
  //general stuff
  r.drawLine(...);
  //only possible on directX
  if(DirectxRenderer rx = dynamic_cast<DirectxRenderer*>(r)) {
  //...
  } else {
    //throw exception or do fallback rendering in case we use something else
  }
} 
于 2010-07-14T20:52:53.623 に答える
1

テンプレートを使用すると、IRendableを2つのクラスに分割できます。2つのレンダラータイプのそれぞれに1つずつです。これはおそらく最良の答えではありませんが、動的キャストの必要性を回避します。

template <typename RendererType>
struct IRenderable {
    virtual void Render(RendererType* renderer) const = 0;
}

template <typename RendererType>
class AbstractGeometricalObject : public IRenderable<RendererType> {
    virtual void Render(RendererType* renderer) const { ... }
};

class DirectxGeometricalObject : public AbstractGeometricalObject<DirectxRenderer> {
  // this class will now require a void Render(DirectxRenderer* renderer)
}
于 2010-07-14T23:18:44.503 に答える
0

コンパイラから離れて、理論を考えてみましょう。がパラメータとしてDirectxGeometricalObject::Render期待DirectxRendererし、何も期待しない場合AbstractRenderer、他の誰かOtherGeometricalObject::RenderがおそらくOtherRendererオブジェクトをパラメータとして期待します。

したがって、の実装AbstractGeometricalObjectが異なれば、メソッドのシグネチャも異なりRenderます。それらが異なる場合、仮想を定義する目的はありませんAbstractGeometricalObject::Render

を宣言するAbstractGeometricalObject::Render(AbstractRenderer*)と、任意のレンダラーを任意の幾何学的オブジェクトに渡すことができるはずです。あなたの場合、失敗するのでできませdynamic_castん。

于 2010-07-14T23:58:14.987 に答える
0

セッターを使用してレンダラー変数を設定し、その1か所で適切なタイプにキャストします。

于 2010-07-14T20:35:12.243 に答える
0

Bridgeデザインパターンが役立つかどうかを確認します。「抽象化をその実装から切り離して、2つを独立して変化させることができるようにします。」この例では、AbstractGeometricalObjectは、プラットフォーム固有のサブクラスを持つ純粋な仮想インターフェイスである実装を指します。トリッキーな部分は、そのインターフェイスを発見するために時間をかけることです。

于 2010-07-14T21:37:36.370 に答える