4

共通のベースから派生したさまざまなオブジェクトの束をファイルに保存して再読み込みしています。明らかに、再読み込み時に正しいオブジェクト タイプを作成するために、クラス名 (または同様のもの) を保存する必要があります。

保存は簡単です:

class Base 
{
  virtual string className() const = 0;

  void saveToFile()
  {
    write(className());
    ... other writing stuff
  }
}

class Derived1: public Base 
{
  string className() const { return "Derived1"; };
  ...
}

class Derived2: public Base 
{
  string className() const { return "Derived2"; };
  ...
}

そして、文字列を複製することを気にしなければ、ロードは簡単です...

static Base * Base::factory(const String &cname)
{
  if (cname == "Derived1")
    return new Derived1; 
  else if (cname == "Derived2")
    return = new Derived2; 
  else ...
}

void load()
{
  String cname = readString();

  Base * obj(Base::factory(cname);

  obj->readIt();
}

But, the duplicated strings offends my sense of DRY: Ideally, className() could be static virtual but that isn't allowed. I have a feeling that I'm missing an obvious 'clean' way round this, but I can't see it yet. Any suggestions?

Note: OK, code slightly tweaked using a factory method. Note that this doesn't actually answer the problem!

Note #2: The code above isn't trying to be a perfect representation of the ultimate factory pattern. I'm not concerned with unwanted linkage between base and derived classes, or potential difficulties in extending the hierarchy. I'm looking for answers that will simplify the code rather than complicate it.

4

6 に答える 6

3

ここで2つのこと。まず、名前を 2 回書き出す必要がないように、過去に次のようなものを使用しました。

class Derived : public Base
{
    //  ...
    static char const* className() { return "Derived"; }
    virtual char const* getClassName() const { return className(); }
                 //  overrides virtual function in the base class...
};

さらに、外部ソースからクラスを読み取れるようにしたい場合は、そのような関数のマップに自分自身を登録する、ある種の静的ファクトリ関数が必要になります。私は先に進み、これを次のように行いますBase:

class Base
{
    // ...
protected:
    class Factory
    {
    protected:
        Factory( std::string const& type );
    public:
        virtual Base* constructFromFile( std::istream const& source ) const = 0;
    };
    typedef std::map <std::string, Factory const*> FactoryMap;
    static FactoryMap& factories();

    template <typename Derived>
    class ConcreteFactory : public Factory
    {
    public:
        ConcreteFactory() : Factory( Derived::className() ) {}
        virtual Base* constructFromFile( std::istream const& source ) const
        {
            return new Derived( source );
        }
    };

public:
    static Base* readFromFile( std::istream& source );
};


Base::FactoryMap&
Base::factories()
{
    static FactoryMap theOneAndOnly;
    return theOneAndOnly;
}

Base::Factory::Factory( std::string const& type )
{
    std::pair <FactoryMap::iterator, bool> results 
        = factories().insert( std::make_pair( type, this ) );
    assert (results.second);
}

Base* Base::readFromFile( std::istream& source )
{
    std::string type = readType( source );
    FactoryMap::const_iterator factory = factories().find( type );
    if ( factory == factories().end() ) {
        throw UnknownType(...);
    }
    return factory->second->constructFromFile( std::istream& source );
}

最後に、派生クラスごとにstd::istream&、 と の静的インスタンスを受け取る コンストラクターを定義する必要がありますBase::ConcreteFactory <Derived>。(上記のように、これは静的メンバーでなければなりません。)

于 2012-09-17T13:27:45.163 に答える
3

ifそれはあなたができる最善のことです.ファクトリクラスでラップすることで、少しきれいにすることができます.

于 2012-09-17T12:26:15.820 に答える
2

Id'は、クラスを作成する関数への文字列のマップを作成します。

std::hash_map<std::string, std::function<Base*()> creators;

次に、マップを塗りつぶす関数を作成できます

template <typename T> void add()
{
    creators.insert(std::pair(T::class_name(), []()-> Base* { return new T(); }));
}

使用法は簡単です:

//factory constructor
add<Derived1>();
add<Derived2>();

//creation
Base* r = 0;
auto it = creators.find(string);
if (it != creators.end()) {
    r = (*it)();
}
于 2012-09-17T13:13:38.877 に答える
2

明らかにFactoryパターンが必要です。ここで詳細を読む http://www.codeproject.com/Articles/363338/Factory-Pattern-in-Cplusplus

于 2012-09-17T12:26:51.300 に答える
2

ファクトリ メソッド (AKA バーチャル コンストラクター) を使用できます。これについては、デザイン パターンの本やインターネットのさまざまな場所で説明されています。これらの用語で Google 検索を行うことができます。これについては、StackOverflow で既に議論されている可能性があります。

于 2012-09-17T12:30:13.173 に答える
0

私は同じ問題に遭遇し、この記事でかなりきちんとした答えを見つけました: ジェームズ・カンゼの答えに十分近い工業用強度のプラグ可能な工場ですが、これを読んで自分で試してみることをお勧めします。

于 2012-09-18T13:09:53.083 に答える