2

私はC++プロジェクトにファクトリデザインパターンを適用しています。以下に、私がどのように行っているかを示します。「anti-if」キャンペーンに従ってコードを改善しようとしているので、私が持っているifステートメントを削除したいと思います。どうすればいいですか?

typedef std::map<std::string, Chip*> ChipList;

Chip* ChipFactory::createChip(const std::string& type) {
    MCList::iterator existing = Chips.find(type);
    if (existing != Chips.end()) {
        return (existing->second);
    }
    if (type == "R500") {
        return Chips[type] = new ChipR500();
    }
    if (type == "PIC32F42") {
        return Chips[type] = new ChipPIC32F42();
    }
    if (type == "34HC22") {
        return Chips[type] = new Chip34HC22();
    }
    return 0;
}

文字列をキーとして、コンストラクター(またはオブジェクトを作成するための何か)を使用してマップを作成することを想像します。その後、タイプ(タイプは文字列)を使用してマップからコンストラクターを取得し、ifなしでオブジェクトを作成できます。(私は少しパラノイアになっていることは知っていますが、それができるかどうか知りたいです。)

4

8 に答える 8

7

そうです、キーから作成関数へのマップを使用する必要があります。あなたの場合、それは

typedef Chip* tCreationFunc();
std::map<std::string, tCreationFunc*> microcontrollers;

新しいチップ駆動クラス ChipXXX ごとに、静的関数を追加します。

static Chip* CreateInstance()
{
    return new ChipXXX();
}

また、この関数をマップに登録します。

あなたのファクトリ関数は、次のようなものでなければなりません:

Chip* ChipFactory::createChip(std::string& type)
{
    ChipList::iterator existing = microcontrollers.find(type);    
    if (existing != microcontrollers.end())
        return existing->second();

    return NULL;
}

あなたの例のように、コピーコンストラクターは必要ないことに注意してください。

于 2010-08-17T11:43:05.593 に答える
5

ファクトリのポイントは、if を取り除くことではなく、実際のビジネス ロジック コードの別の場所に配置し、それを汚染しないことです。それは単なる関心の分離です。

于 2010-08-17T11:33:53.633 に答える
2

もしあなたが必死なら、if ステートメントなしでこの仕事をする jump table/clone() コンボを書くことができます。

class Factory {
    struct ChipFunctorBase {
        virtual Chip* Create();
    };
    template<typename T> struct CreateChipFunctor : ChipFunctorBase {
        Chip* Create() { return new T; }
    };
    std::unordered_map<std::string, std::unique_ptr<ChipFunctorBase>> jumptable;
    Factory() {
        jumptable["R500"] = new CreateChipFunctor<ChipR500>();
        jumptable["PIC32F42"] = new CreateChipFunctor<ChipPIC32F42>();
        jumptable["34HC22"] = new CreateChipFunctor<Chip34HC22>();
    }
    Chip* CreateNewChip(const std::string& type) {
        if(jumptable[type].get())
            return jumptable[type]->Create();
        else
            return null;
    }
};

ただし、この種のアプローチは、多数の異なる種類のチップがある場合にのみ価値があります。いくつかの場合は、いくつかの if を書くだけの方が便利です。

簡単なメモ: std::unordered_map と std::unique_ptr を使用しましたが、コンパイラの新しさによっては、STL の一部ではない場合があります。std::map/boost::unordered_map および std::/boost::shared_ptr に置き換えます。

于 2010-08-17T11:45:26.607 に答える
1

あなたの質問に答えるには: はい、必要なオブジェクトを構築する関数へのマップを持つファクトリを作成する必要があります。構築されたオブジェクトは、その機能を提供し、ファクトリ自体に登録する必要があります。

他のいくつかのSOの質問にもこの件に関する読み物があるので、ここですべてを説明するのではなく、それを読んでもらいます。

C++ のジェネリック ファクトリ

クラス名を保持する文字列からオブジェクトをインスタンス化する方法はありますか?

于 2010-08-17T12:15:53.660 に答える
1

いいえ、ifs を取り除くことはできません。createChip メソッドは、引数として渡す定数 (型名) に応じて新しいインスタンスを作成します。ただし、if ステートメントからこれらの 2 行を少し削除して、コードを最適化することができます。

 microcontrollers[type] = newController;
 return microcontrollers[type];
于 2010-08-17T11:39:22.487 に答える
0

ファクトリにsを含めることができifます。コード全体にsを散らかさないでください。

于 2010-08-17T11:30:38.827 に答える
0

基本的に、あなたが求めているのは と呼ばれるものですVirtual Construction。つまり、型が実行時にしか分からないオブジェクトを構築する能力です。

もちろん、C++ ではコンストラクターを仮想にすることはできないため、これには少しコツが必要です。一般的な OO アプローチは、次のPrototypeパターンを使用することです。

class Chip
{
public:
  virtual Chip* clone() const = 0;
};

class ChipA: public Chip
{
public:
  virtual ChipA* clone() const { return new ChipA(*this); }
};

次に、これらのプロトタイプのマップをインスタンス化し、それを使用してオブジェクトを構築します ( std::map<std::string,Chip*>)。通常、マップはシングルトンとしてインスタンス化されます。

もう 1 つの方法は、これまで説明してきたように、オブジェクトではなく直接メソッドを登録することと似ています。deleteそれはあなたの個人的な好みかもしれませんが、一般的にはわずかに高速であり (それほど多くはありません。仮想ディスパッチを回避するだけです)、メモリの処理が簡単です (関数へのポインターを処理する必要はありません)。

ただし、注意する必要があるのは、メモリ管理の側面です。リークしたくないので、必ず RAII イディオムを使用してください。

于 2010-08-17T12:19:34.543 に答える
0
struct Chip{
};

struct ChipR500 : Chip{};

struct PIC32F42 : Chip{};

struct ChipCreator{
   virtual Chip *make() = 0;
};

struct ChipR500Creator : ChipCreator{
   Chip *make(){return new ChipR500();}
};

struct PIC32F42Creator : ChipCreator{
   Chip *make(){return new PIC32F42();}
};

int main(){
   ChipR500Creator m;  // client code knows only the factory method interface, not the actuall concrete products
   Chip *p = m.make();
}
于 2010-08-17T11:38:41.353 に答える