ファクトリメソッドの実装-C ++
-
03-07-2019 - |
質問
" factory"には次のコードがあります。デザインパターンの実装。
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 ++テンプレート」を使用することでより良い方法で実現できると聞きました。誰もがそれがどのように行われ、テンプレートアプローチがこれよりも優れているかを助けることができますか?
考え
解決
投稿した例では、ファクトリアプローチもテンプレートアプローチも意味がありません。 私のソリューションには、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に合わせてカラークラスを調整する必要があります。おそらく、これよりもはるかに高度なものにする必要があります(異なるカラースペースなど)。
テンプレートではない理由
テンプレートを使用する意味がないのは、(おそらく)異なる描画操作の唯一の違いが色変数だからです。そのため、テンプレートを使用して(または手動で別のクラスを宣言します)、同様のコードを複製します。これにより、プログラムが大きくなり、速度が低下します。
そのため、描画関数は引数として色を使用するか、(この例のように)クラスデータメンバーとして色を使用する必要があります。
他のヒント
別の方法は、 creator 関数を動的なFactoryオブジェクトに動的に登録することです。
BluePen *create_BluePen() { return new BluePen; }
static bool BluePen_creator_registered =
Factory::instance()->registerCreator("BluePen",
create_BluePen);
このようにすることで興味深い効果の1つは、静的bool変数 BluePen-creator-registered
が main()
が開始する前に設定され、登録が自動化されることです。
これらの行は、通常のマクロを通じて作成される場合があります。つまり、
#define METAIMPL( _name ) \
_name *create_ ## _name() { return new _name; } \
static bool _name ## _creator_registered = \
Factory::instance()->registerCreator(# _name, \
create_ ## _name)
...およびコンストラクターの近くで使用
METAIMPL( BluePen ); // auto registers to the Factory
BluePen::BluePen() : Pen() {
// something
}
その後、ファクトリのタスクは、これらの creator 関数を保存および検索することです。残りは演習のままにしておきます;)METADECLマクロの使用
詳細情報が必要な場合は、こちらをご覧ください。 4.1メタ情報の章で、インスペクタ機能の可能性を含めるように拡張する方法も含まれています
古いMacAppをC ++に移植するプロジェクトである ET ++ を使用して、これを学びましたおよびX11。その努力の中で、エリックガンマなどはデザインパターン
について考え始めました。そして...(2011年5月7日)最後にgithubに例をプッシュしました
https ://github.com/epatel/cpp-factory
あなたの工場は大丈夫です。私はそれを BluePen
に取りました。これは単なるクラス名の例です。次の条件が満たされている場合、テンプレートを使用できます。
コンパイル時に(つまり、コードを記述するときに)特定の型を返すことがわかっている場合は、テンプレートを使用します。それ以外の場合はできません。
これは、コードで次のことができることを意味します。
template<typename PenType>
auto_ptr<Pen> createPen(){
return auto_ptr<Pen>(new PenType);
}
それを所定の場所に置いたら、次のように使用できます
...
auto_ptr<Pen> p = createPen<BluePen>();
...
ただし、テンプレート引数の BluePen
は、実行時に型に設定される変数にはできません。この例では、実行時に設定できる文字列を渡します。したがって、C ++テンプレートを使用できることを読んだとき、その推奨事項は条件付きでのみ当てはまります。次に、作成するペンの決定がコンパイル時に既に行われている場合。その条件に当てはまる場合、テンプレートソリューションは行うべき 正しいことです。実行時にコストがかかることはなく、まさに必要なものになります。
色の特別な空のクラスを宣言することにより、テンプレートを使用してすべてを実行できます。これには、コンパイル時にすべての色を選択可能にする必要があります。これにより、仮想メソッドで基本クラスを使用する必要がなくなります。
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 >());
}
ジェネリックオブジェクトファクトリクラスをテンプレートクラスとして記述できます(または、この gamedev.netの記事)。
この方法では、コードに複数のファクトリーがある場合、各ファクトリーを定義する手間が少なくなります。
もう1つの答えの補足として、Factoryパターンとテンプレートの使用法について説明します。
テンプレートを使用する主な(そして最も単純な)理由は、使用するデータ型を除いて、コードがすべての場合で同一であることです。ここでの例は、STLコンテナーです。 ファクトリー関数createVector(&quot; string&quot;)を記述し、各コンテナーを手動で入力することも可能ですが、これは明らかに最適ではありません。
データ型だけでなく、コードが異なる場合でも、テンプレートの特殊化を使用できますが、多くの場合、ファクトリー関数の方が意味があります。
例として、データベース抽象化ライブラリを考えます。ライブラリを&quot; db :: driver&quot;のように使用できるように、テンプレートの特殊化を使用することができます。ただし、これにより、コード内のあらゆる場所にデータベースタイプを入力するか(最初はライブラリがまったく役に立たないようにするか...)、db :: driverクラスのインターフェイスタイプに対してケースを実行することになります。
この例では、db :: get_driver(odbc)と言って、インターフェイス型にキャストされた適切なクラスを取得する方がより直感的です。