ライブラリの分離を可能にしながら、多態性動作の設計パターン


10

Itemクラスの階層があるとしましょう:Rectangle, Circle, Triangle。それらを描画できるようにしたいので、私の最初の可能性はDraw()それぞれに仮想メソッドを追加することです:

class Item {
public:
   virtual ~Item();
   virtual void Draw() =0; 
};

ただし、コアライブラリには基本的な表現しか含まれていないのに、描画機能を別のDrawライブラリに分割したいと思います。私が考えることができるいくつかの可能性があります:

1-のDrawManagerリストを受け取り、何をすべきかを計算Itemするdynamic_cast<>ために使用する必要がある:

class DrawManager {
  void draw(ItemList& items) {
    FOREACH(Item* item, items) {
       if (dynamic_cast<Rectangle*>(item)) {
          drawRectangle();
       } else if (dynamic_cast<Circle*>(item)) {
          drawCircle();
       } ....
    }
  }
};

これはRTTIに依存し、1つのクラスが階層内のすべての項目を認識するように強制するため、理想的ではありません。

2-もう1つのアプローチは、描画の責任をItemDrawer階層に任せることです(RectangleDrawerなど)。

class Item {
   virtual Drawer* GetDrawer() =0;
}

class Rectangle : public Item {
public:
   virtual Drawer* GetDrawer() {return new RectangleDrawer(this); }
}

これにより、Itemの基本表現と描画用コードの間の関心の分離が実現します。ただし、問題は、Itemクラスが描画クラスに依存していることです。

この描画コードを別のライブラリに分離するにはどうすればよいですか?Itemsが何らかの説明のファクトリクラスを返す解決策はありますか?ただし、コアライブラリが描画ライブラリに依存しないように、これをどのように定義できますか?

回答:


3

ビジターパターンを見てみましょう。

その目的は、アルゴリズム(あなたの場合は描画)を、それが動作するオブジェクト構造(アイテム)から分離することです。

したがって、問題の簡単な例は次のようになります。

class Item
{
public:
    virtual void visit(Visitable v)
    {
        v.accept(*this)
    }
};

class Rectangle : public Item
{
public:
    virtual void visit(Visitable v)
    {
        v.accept(*this)
    }
};

class Circle : public Item
{
public:
    virtual void visit(Visitable v)
    {
        v.accept(*this)
    }
};


class Visitable
{
public:
    virtual void accept(Rectangle& r);
    virtual void accept(Circle& c);
};

class Drawer : public Visitable
{
public:
    void accept(Rectangle& r)
    {
        // draw rectangle
    }   
    void accept(Circle& c)
    {
        // draw circle
    }
};


int main()
{
    Item* i1 = new Circle;
    Item* i2 = new Rectangle;

    Drawer d;
    i1->visit(d); // Draw circle
    i2->visit(d); // Draw rectangle

    return 1;
}

興味深い-多態性を実現するためにオーバーロードを伴うビジターを使用することを考えたことはなかった。これは実行時に正しくオーバーロードしますか?
the_mandrill

はい、正しくオーバーロードします。編集された回答を見る
hotpotato

これが機能することがわかります。ただし、各派生クラスがvisit()メソッドを実装する必要があるのは残念です。
the_mandrill 2013

これは私にもう少し検索をするように促しました、そして私は今私が探しているものと思われるロキのビジターパターンの実装を見つけました!私はツリー内のノードを訪問するという点でビジターパターンに精通していましたが、もっと抽象的な方法でそれを考えていませんでした。
the_mandrill

2

説明する2つの並列階層への分割は、本質的にはブリッジパターンです。

洗練された抽象化(Rectangleなど)が実装(RectangleDrawer)に依存することを心配していますが、それを完全に回避する方法がわかりません。

確かに、その依存関係を解消する抽象ファクトリーを提供できますが、作成するサブクラスをファクトリーに指示する何らかの方法が必要であり、そのサブクラスは、特定の洗練された実装に依然依存しています。つまり、RectangleがRectangleDrawerに依存していなくても、ファクトリにそれを作成するように指示する方法が必要であり、RectangleDrawer自体は依然としてRectangleに依存しています。

これが有用な抽象化であるかどうかを確認することも価値があります。長方形を描くのが難しいので、別のクラスを配置する価値がありますか、それとも本当にグラフィックライブラリを抽象化しようとしていますか?


並列階層を持つ前にそのパターンを見たと思っていました。そのことについて考えてくれてありがとう。グラフィックライブラリを抽象化したいのですが。コアライブラリがグラフィックライブラリに依存する必要はありません。コアアプリケーションがシェイプオブジェクトを管理し、それらを表示するためのライブラリがアドオンであるとしましょう。ただし、これは実際には四角形を描画するためのアプリケーションではないことを覚えておいてください。単にメタファーとして使用しているだけです。
the_mandrill

私が考えているのは、グラフィックライブラリを抽象化するための最も有用な方法ではないRectangleDrawerCircleDrawerなどです。代わりに、通常の抽象インターフェースを介してプリミティブのセットを公開しても、依存関係は解消されます。
役に立たない

1

見ていvalueセマンティクスと概念ベースの多型を

この作品は私がポリモーフィズムの見方を変えました。このシステムを使用して、繰り返し発生する状況に大きな影響を与えています。


これは魅力的に見えます-私が望む方向に向かっているようです
the_mandrill

1
リンクが壊れています。このため、本質的にリンクのみの回答は推奨されません。
ajb

0

問題が発生するのは、オブジェクトが自分自身を描画してはならないためです。何かを正しく描画することは10億個の状態に依存し、Rectangleにはそれらの状態はありません。バックバッファー/深度バッファー/シェーダー/などのコア状態を表す中央のグラフィカルオブジェクトを用意し、それにDraw()メソッドを指定する必要があります。


オブジェクトは自分自身を描画するべきではないことに同意します。したがって、関心の分離を実現するために、その能力を別のクラスに移動したいという欲求です。これらのクラスがあったとDogCat-それらを描画するオブジェクトが矩形を描画するためのより多くの、より複雑です。
the_mandrill
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.