基本クラスBのオブジェクトを構築するためのファクトリクラスがあります。このファクトリを使用するオブジェクト(D)は、実際のタイプを表す文字列のリストを受け取ります。正しい実装は何ですか:
- ファクトリは列挙型を受け取り(そしてCreate関数内でスイッチを使用し)、Dは文字列を列挙型に変換する責任があります。
- ファクトリは文字列を受け取り、有効な文字列のセットとの一致をチェックします(ifs'を使用)
- 私が考えていなかった他の実装。
基本クラスBのオブジェクトを構築するためのファクトリクラスがあります。このファクトリを使用するオブジェクト(D)は、実際のタイプを表す文字列のリストを受け取ります。正しい実装は何ですか:
文字列から列挙型への変換を個別のオブジェクトに分離します。これは地図で簡単に解決できます。しかし、エラー処理などは、D もファクトリも心配する必要のないものです。
次に、D がコンバーターを呼び出してその列挙型を取得するか、既に変換されているため、D は列挙型をファクトリに渡すだけで済みます。(ちなみに、工場は内部的にスイッチの代わりにマップを使用する方がよいでしょう)。
これにより、疑問が生じます: 実際に列挙型が必要ですか (D とファクトリ以外の場所で)? そうでない場合は、列挙型を除外して、マップを使用して文字列から型に直接変換することができます (つまり、C++ は動的クラスの読み込みをサポートしていないため、必要な具象型インスタンスを作成する関数オブジェクトに変換します)。あなた)。大まかな例 (テストするための IDE を持っていないので、エラーがある場合はご容赦ください):
// Function type returning a pointer to B
typedef (B*)(*func)() StaticConstructor;
// Function creating instances of subclass E
B* createSubclassE() {
return new E(...);
}
// Function creating instances of subclass F
B* createSubclassF() {
return new F(...);
}
// Mapping from strings to constructor methods creating specific subclasses of B
map<string, StaticConstructor> factoryMap;
factoryMap["E"] = &createSubclassE;
factoryMap["F"] = &createSubclassF;
もちろん、作成されたインスタンスも適切に破棄する必要があります。製品コードでは、返されたオブジェクトは、たとえばauto_ptr
. しかし、この短い例で基本的な考え方を十分に理解していただければ幸いです。さらに必要な場合は、ここにチュートリアルがあります...
通常の方法は、ファクトリをシングルトンにすることです。次に、クラスBに基づく各クラスは、静的初期化時に作成関数と名前をファクトリに登録します。これは多くの場合、マクロで行われます。次に、ファクトリはこれらの名前の高速ハッシュテーブルを作成して関数を作成できます。など...あなたはドリフトを取得します。
一致するすべての文字列をセットまたはリストに入れて、ifs/switches を記述する代わりに、文字列が含まれているかどうかを確認できます。
私のプロジェクトにVC++/Qt
は、ソースに Enum 表現を持つ文字列を含む多数の XML ファイルがありました。
QString
したがって、各 Enum に対して、オーバーロードされた演算子<>を持つラッパーがありましたEnum
。
enum DataColumnTypeEnum
{
DataColumnTypeNotSet,
ColumnBinary,
ColumnBoolean,
ColumnDate,
ColumnDateTime,
ColumnNumber,
ColumnFloat,
ColumnPrimary,
ColumnString,
ColumnText,
};
class DataColumnType
{
public:
DataColumnType();
DataColumnType(DataColumnTypeEnum);
DataColumnType(const QString&);
DataColumnType& operator = (DataColumnTypeEnum);
DataColumnType& operator = (const QString&);
operator DataColumnTypeEnum() const;
operator QString() const;
private:
DataColumnTypeEnum type;
};
DataColumnType& DataColumnType::operator = (const QString& str)
{
str.toLower();
if(str.isEmpty()) type = DataColumnTypeNotSet;
else if(str == "binary") type = ColumnBinary;
else if(str == "bool") type = ColumnBoolean;
else if(str == "date") type = ColumnDate;
else if(str == "datetime") type = ColumnDateTime;
else if(str == "number") type = ColumnNumber;
else if(str == "float") type = ColumnFloat;
else if(str == "primary") type = ColumnPrimary;
else if(str == "string") type = ColumnString;
else if(str == "text") type = ColumnText;
return *this;
}
しかし、最後のリストのアプローチは非常に醜いです。
静的ハッシュ テーブルまたは辞書を作成して調べたほうがよいでしょう。
私は個人的に強化された列挙型を使用しています。これは、C++ の列挙型が不足していることに常に気付いているためです。次のようなメッセージType 3 - method -begin
はあまり有益ではありません。
このように、単純なテンプレート クラスを使用します。
template <class Holder>
class Enum
{
public:
typedef typename Holder::type enum_type;
Enum(): mValue(Invalid()) {}
Enum(enum_type i): mValue(Get(i)) {}
explicit Enum(const std::string& s): mValue(Get(s)) {}
bool isValid() const { return mValue != Invalid(); }
enum_type getValue() const { return mValue->first; }
private:
typedef typename Holder::mapping_type mapping_type;
typedef typename mapping_type::const_iterator iterator;
static const mapping_type& Mapping() { static mapping_type MMap = Holder::Initialize(); return MMap; }
static iterator Invalid() { return Mapping().end(); }
static iterator Get(enum_type i) { // search }
static iterator Get(const std::string& s) { // search }
iterator mValue;
};
Holder
次のように定義します。
struct Example
{
typedef enum {
Value1,
Value2,
Value3
} type;
typedef std::vector< std::pair< type, std::string > > mapping_type;
static mapping_type Initialize() {
return builder<mapping_type>()(Value1,"Value1")(Value2,"Value2")(Value3,"Value3");
}
};
そのためのマクロを定義できます。
DEFINE_ENUM(Example, (Value1)(Value2)(Value3))
しかし、私は練習として実装を許可します (Boost.Preprocessor
あなたの友人です)。
クールなのはそれを使用することです!
int main(int argc, char* argv[])
{
std::string s;
std::cin >> s;
Enum<Example> e(s);
switch(e.getValue())
{
case Example::Value1:
case Example::Value2:
++e;
case Example::Value3:
std::cout << e << std::endl;
default:
}
}