1

同じ階層に異なるクラスを作成するファクトリ関数を検討しています。通常、ファクトリは通常次のように実装されることを理解しています。

Person* Person::Create(string type, ...)
{
    // Student, Secretary and Professor are all derived classes of Person
    if ( type == "student" ) return new Student(...);
    if ( type == "secretary" ) return new Secretary(...);
    if ( type == "professor" ) return new Professor(...);
    return NULL;
}

さまざまな条件をハードコードする必要がないように、プロセスを自動化できる方法を考えようとしています。

これまでのところ、私が考えることができる唯一の方法は、マップとプロトタイプ パターンを使用することです。

マップは、最初の要素に型文字列を保持し、2 番目の要素にクラス インスタンス (プロトタイプ) を保持します。

std::map<string, Person> PersonClassMap;
// This may be do-able from a configuration file, I am not sure
PersonClassMap.insert(make_pair("student", Student(...)));
PersonClassMap.insert(make_pair("secondary", Secretary(...)));
PersonClassMap.insert(make_pair("professor", Professor(...)));

関数は次のようになります。

Person* Person::Create(string type)
{
    map<string, Person>::iterator it = PersonClassMap.find(type) ;
    if( it != PersonClassMap.end() )
    {
        return new Person(it->second); // Use copy constructor to create a new class instance from the prototype.
    }
}

残念ながら、プロトタイプ メソッドは引数をサポートしていないため、ファクトリによって作成されるクラスを毎回同じにしたい場合にのみ機能します。

いい方法でそれを行うことが可能かどうか誰かが知っていますか、それともファクトリ関数にこだわっていますか?

4

7 に答える 7

1

クラス Person に純粋な抽象cloneメソッドを追加します(これは、主にサブクラス化するために存在する抽象クラスである必要があるように見えます-具体的な「上記のいずれでもない」種類の Person が必要な場合は、基本クラス自体としてではなく、別の具体的なサブクラス OtherKindOfPerson として):

virtual Person* clone() const = 0;

newそして、特定の具象サブクラスのコピー ctor を呼び出すa を使用して、Student などのすべての具象サブクラスでオーバーライドします。

Person* clone() const { return new Student(*this); }

また、レジストリ マップを次のように変更する必要があります。

std::map<string, Person*> PersonClassMap;

[[単純な古いポインターよりもスマートなポインターを使用できますPerson *が、マップとそのすべてのエントリは、プロセスが存続する限りおそらく存続する必要があるため、これは大したことではありません-得られる主な付加価値「ポインタ」の破壊時によりスマートな動作であるよりスマートなポインタ!-)]]

これで、ファクトリ関数は次のように単純に終了できます。

return it->second->clone();

追加の属性を持つサブクラスで基本クラスのコピー ctor を使用することによる「スライス」効果を回避し、仮想メソッドの解決を保持するために、変更が必要です。

具象クラスをサブクラス化して他の具象クラスを生成することは、これらの効果が扱いにくく、バグの原因となる可能性があるため、まさに悪い考えです ( Haahrの推奨事項を参照してください。彼は Java について書いていますが、アドバイスは C++ や他の言語にも適しています)。 [実際、C++ では彼の推奨事項がさらに重要であることがわかりました!].

于 2009-07-04T00:55:28.790 に答える
1

ファクトリ メソッドを登録できます (コピーするビルド済み要素の代わりに)。これにより、具象ファクトリに渡されるパラメータを使用して抽象ファクトリを呼び出すことができます。ここでの制限は、すべてのコンクリート ファクトリのパラメータ セットが同じでなければならないことです。

typedef std::string discriminator;
typedef Base* (*creator)( type1, type2, type3 ); // concrete factory, in this case a free function
typedef std::map< discriminator, creator > concrete_map;
class Factory // abstract
{
public:
   void register_factory( discriminator d, creator c ) {
      factories_[ d ] = c;
   }
   Base* create( discriminator d, type1 a1, type2 a2, type3 a3 )
   {
      return (*(factories_[ d ]))( a1, a2, a3 );
   }
private:
   concrete_map factories_;
};

無料の関数クリエーターを使用してサンプル コードを縮小しましたが、型を定義してconcrete_factory上記の「creator」要素の代わりに使用できます。繰り返しになりますが、ご覧のとおり、ファクトリの 'create' メソッドでは固定の引数セットに制限されています。

各具象ファクトリは、指定された型のコンストラクターに引数を渡すことができます。

Base* createDerived1( type1 a1, type2 a2, type3 a3 )
{
   return new Derived1( a1, a2, a3 );
}

これは、外部オブジェクト (構築中にのみ初期化できる) または定数メンバーへの参照を保持するインスタンス、または構築後に別の状態にリセットできないオブジェクトをより一般的な表現で作成できるため、アプローチよりも柔軟です。

于 2009-07-03T22:33:41.277 に答える
0

各タイプの人の列挙型を作成できます。

enum PersonType { student, secretary, professor };
于 2009-07-04T01:19:55.670 に答える