4

私の質問は、オブジェクト作成で列挙型を削除してステートメントを切り替える必要があるデザインパターンとほぼ同じですが、抽象ファクトリパターンがここにうまく適合しているとは思いません。

私は現在、いくつかの既存の DAL/ORM 混合ライブラリのリファクタリング/再実装を計画しています。既存のコードのどこかに、次のようなコードがあります。

class Base
{
  static Base * create(struct Databasevalues dbValues)
  {
    switch(dbValues.ObjectType)
    {
    case typeA:
      return new DerivedA(dbValues);
      break;
    case typeB:
      return new DerivedB(dbValues);
      break;
    }
  }
}

class DerivedA : public Base
{
  // ...
}

class DerivedB : public Base
{
  // ...
}

したがって、データベース通信を担当するライブラリは、構造体にデータベース エンティティに関するすべての情報を入力し、次に上記の create() メソッドを呼び出して、対応するオブジェクトを ORM に実際に作成します。しかし、基本クラスがそのすべての派生クラスを知っているという考えは好きではありません。また、switch ステートメントも好きではありません。また、これらのオブジェクトを作成するためだけに別のクラスを作成することも避けたいと思います。現在のアプローチについてどう思いますか?この機能をどのように実装しますか?

4

4 に答える 4

7

これはここで何百万回も議論されてきました。別のファクトリクラスを作成したくない場合は、これを行うことができます。

class Base
{

public:
    template <class T>
    static void Register  (TObjectType type)
    {
       _creators[type] = &creator<T>;
    }

    static Base* Create (TObjectType type)
    {
        std::map <TObjectType, Creator>::iterator C = _creators.find (type);
        if (C != _creators.end())
          return C->second ();

        return 0;        
    }

private:
    template <class T>
    static Base* creator ()
    {
        return new T;
    }

private:
    typedef Base* (::*Creator) ();
    static std::map <TObjectType, Creator> _creators;
};

int main ()
{
   Base::Register <Derived1> (typeA);
   Base::Register <Derived2> (typeB);

   Base* a = Base::Create (typeA);
   Base* b = Base::Create (typeB);
}
于 2012-11-08T16:22:26.143 に答える
3

スイッチを のようなマッピングに置き換えたとしましょうmap<ObjectType, function<Base* (DatabaseValues&)>>

現在、ファクトリ (基本クラスに存在する場合と存在しない場合があります) は、すべてのサブクラスについて知る必要はありません。

ただし、何らかの方法でマップを作成する必要があります。これは、何かがそれに値を設定するか (つまり、すべてのサブクラスの問題について知っていることが、ある場所から別の場所にプッシュされただけです)、またはサブクラスが静的初期化を使用してファクトリ関数をマップに登録する必要があることを意味します。

于 2012-11-08T16:17:09.223 に答える
1

列挙型を使用しないでください。それらは OO 構造ではないため、JAVA が最初にそれらを持っていませんでした (残念ながら、それらを追加するには圧力が大きすぎました)。

そのような列挙型の代わりに検討してください:

enum Types {
  typeA,
  typeB
};

この構造は、スイッチ (私の意見では別の非 OO 構造) とマップを必要としません:

タイプ.h

class Base;
class BaseFactory {
public:
   virtual Base* create() = 0;
};     
class Types {
public:
  // possible values 
  static Types typeA;
  static Types typeB;
  // just for comparison - if you do not need - do not write...
  friend bool operator == (const Types & l, const Types & r) 
  { return l.unique_id == r.unique_id; }
  // and make any other properties in this enum equivalent - don't add them somewhere else 
  Base* create() { return baseFactory->create(); }

private:
   Types(BaseFactory* baseFactory, unsigned unique_id);
   BaseFactory* baseFactory;
   unsigned unique_id; // don't ever write public getter for this member variable!!!
};   

タイプ.cpp

#include "Types.h"
#include "Base.h"
#include "TypeA.h"
#include "TypeB.h"

namespace {
  TypeAFactory typeAFactory;
  TypeBFactory typeAFactory;
   unsigned unique_id = 0;
}
Types Types::typeA(&typeAFactory, unique_id++);
Types Types::typeA(&typeBFactory, unique_id++);

したがって、あなたの例(この関数が本当に必要な場合):

class Base
{
  static Base * create(struct Databasevalues dbValues)
  {
    return dbValues.ObjectType.create();
  }
};

欠けている部分は簡単に実装できるはずです。

于 2012-11-08T16:55:31.000 に答える
1

何をするにしても、同様のロジックを隠すだけの switch-case またはその他の構造が必要になります。

ただし、できることとすべきことは、Base から create メソッドを削除することです。完全に正しいのですが、派生メソッドであることを認識してはいけません。このロジックは、ファクトリやコントローラーなどの別のエンティティに属しています。

于 2012-11-08T16:40:14.030 に答える