3

「工場」設計パターンの実装には、次のコードがあります。

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

class RedPen : public Pen{
public:
     virtual void Draw(){
         cout << "Drawing with red pen" << endl;
     }
};

class BluePen : public Pen{
public:
     virtual void Draw(){
         cout << "Drawing with blue pen" << endl;
     }
};

auto_ptr<Pen> createPen(const std::string color){
     if(color == "red")
         return auto_ptr<Pen>(new RedPen);
     else if(color == "blue")
         return auto_ptr<Pen>(new BluePen);
}

でも「C++のテンプレート」を使えばもっとうまくできると聞きました。それがどのように行われ、テンプレートアプローチがこれよりも優れているかを誰でも助けることができますか?

何かご意見は

4

6 に答える 6

5

あなたの工場は大丈夫です。私はそれを取ります、BluePenなどは単なる例のクラス名でした。次の条件が満たされている場合は、テンプレートを使用できます。

コンパイル時に(つまり、コードを作成するときに)特定の型を返したいことがわかっている場合は、テンプレートを使用します。そうでなければ、あなたはできません。

つまり、コードでは、次のことができるということです。

template<typename PenType>
auto_ptr<Pen> createPen(){
    return auto_ptr<Pen>(new PenType);
}

それが整ったら、次のように使用できます

...
auto_ptr<Pen> p = createPen<BluePen>();
...

ただし、そのテンプレート引数である、はBluePen、実行時に型に設定される変数にすることはできません。あなたの例では、文字列を渡します。これはもちろん実行時に設定できます。したがって、C ++テンプレートを使用できることを読んだ場合、その推奨事項は条件付きでのみ当てはまります。つまり、作成するペンの決定がコンパイル時にすでに行われている場合です。その条件が当てはまる場合は、テンプレートソリューションが適切です。実行時に何も費用がかからず、まさに必要なものになります。

于 2009-01-25T23:03:25.353 に答える
4

あなたが投稿した例では、ファクトリまたはテンプレートのアプローチは私には意味がありません。私のソリューションには、Pen クラスのデータ メンバーが含まれます。

class Pen {
public:
    Pen() : m_color(0,0,0,0) /* the default colour is black */
    {            
    }

    Pen(const Color& c) : m_color(c)
    {
    }

    Pen(const Pen& other) : m_color(other.color())
    {
    }

    virtual void Draw()
    {
        cout << "Drawing with a pen of color " << m_color.hex();
    }
    void setColor(const Color& c) { m_color = c; }
    const Color& color() const { return m_color; }
private:
    Color m_color;
};

class Color {
public:
    Color(int r, int g, int b, int a = 0) :
        m_red(r), m_green(g), m_blue(other.blue()), m_alpha(a)  
    {
    }

    Color(const Color& other) : 
        m_red(other.red()), m_green(other.green()), 
        m_blue(other.blue()), m_alpha(other.alpha())
    {
    }

    int red() const { return m_red; }
    int green() const  { return m_green; }
    int blue() const { return m_blue; }
    int alpha() const { return m_alpha; }

    std::string hex() const
    {
        std::ostringstream os;
        char buf[3];
        os << "#";

        sprintf(buf, "%2X", red());
        os << buf;

        sprintf(buf, "%2X", green());
        os << buf;

        sprintf(buf, "%2X", blue());
        os << buf;

        sprintf(buf, "%2X", alpha());
        os << buf;

        return os.str();
    }

private:
    int m_red;
    int m_green;
    int m_blue;
    int m_alpha;
}

もちろん、カラー クラスは、使用する描画 API に合わせて調整する必要があります。おそらく、これよりもはるかに高度なものにする必要があります (異なるカラー スペースなど)。

なぜテンプレートではないのですか?

テンプレートを使用する意味がない理由は、(おそらく) 異なる描画操作の違いは色変数だけだからです。したがって、テンプレートを使用する (または手動で別のクラスを宣言する) と、同様のコードが複製されます。これにより、プログラムが大きくなり、速度が低下します。

したがって、描画関数は色を引数として受け取るか、(私の例のように) 色をクラス データ メンバーとして持つ必要があります。

于 2009-01-04T11:34:37.730 に答える
3

色の特別な空のクラスを宣言することにより、テンプレートを使用してすべてを行うことができます。これには、コンパイル時にすべての色を選択できるようにする必要があります。これにより、仮想メソッドで基本クラスを使用する必要がなくなります。

struct Red{};
struct Blue{};

template < typename Color >
class Pen{};

template <>
class Pen< Red >
{
     void Draw(){
         cout << "Drawing with red pen" << endl;
     }
};

template <>
class Pen< Blue >
{
     void Draw(){
         cout << "Drawing with blue pen" << endl;
     }
};

template < typename Color >
std::auto_ptr< Pen< Color > > createPen()
{
     return auto_ptr< Pen< Color > >(new Pen< Color >());
}
于 2009-01-30T11:21:34.060 に答える
1

ジェネリック オブジェクト ファクトリ クラスをテンプレート クラスとして記述できます (または、このgamedev.net の記事で適切に説明されているクラスを使用します)。

このように、コードに複数のファクトリがある場合、各ファクトリを定義する作業が少なくなります。

于 2012-02-01T05:43:06.260 に答える
0

私の他の回答の補足として、Factory パターンとテンプレートの使用法について説明します。

テンプレートを使用する主な (そして最も単純な) 理由は、コードが動作するデータ型を除いて、すべてのケースで同一であるためです。ここでの例は、STL コンテナーです。ファクトリ関数 createVector("string") を記述し、各コンテナーを手動で入力することは可能ですが、これは明らかに最適ではありません。

コードが異なる場合でも、データ型だけでなく、テンプレートの特殊化を使用することもできますが、多くの場合、ファクトリ関数の方が理にかなっています。

例として、データベース抽象化ライブラリーを考えてみましょう。ライブラリを「db::driver」のように使用できるように、テンプレートの特殊化を使用することが可能です。ただし、これにより、コードのいたるところにデータベース型を入力する必要があり (そもそもライブラリが役に立たなくなる...)、インターフェイス型 db::driver クラスに対してケースを実行する必要があります。

この例では、db::get_driver(odbc) と言って、インターフェイス型にキャストされた適切なクラスを取得する方が直感的です。

于 2009-01-04T11:43:27.603 に答える