Мой вопрос более или менее идентичен Нужен шаблон проектирования для удаления перечислений и переключения оператора при создании объекта Однако я не вижу, чтобы абстрактный фабричный образец хорошо здесь подходил.
В настоящее время я планирую рефакторинг / повторную реализацию некоторых существующих библиотек 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. Я также хотел бы избежать создания другого класса только с целью создания этих объектов. Что вы думаете о нынешнем подходе? Как бы вы реализовали эту функциональность?
Независимо от того, что вы делаете, вам понадобится либо switch-case, либо какая-либо другая конструкция, которая будет просто скрывать подобную логику.
Однако вы можете и должны удалить метод create из вашей базы — вы совершенно правы, он не должен знать о его производных. Эта логика принадлежит другому объекту, например, фабрике или контроллеру.
Это обсуждалось здесь миллионы раз. Если вы не хотите создавать отдельный класс фабрики, вы можете сделать это.
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);
}
Допустим, вы замените переключатель с отображением, как map<ObjectType, function<Base* (DatabaseValues&)>>
,
Теперь фабрике (которая может или не может жить в базовом классе) не нужно знать обо всех подклассах.
Тем не менее, карта должна быть заполнена как-то. Это означает, что что-то заполняет зная обо всех подклассах проблема была просто перенесена из одного места в другое), или вам нужны подклассы, чтобы использовать статическую инициализацию для регистрации своих фабричных функций на карте.
Просто не используйте перечисления. Они не являются OO конструкцией, поэтому у JAVA их не было в начале (к сожалению, давление было слишком большим, чтобы добавить их).
Рассмотрим вместо такого перечисления:
enum Types {
typeA,
typeB
};
это конструкция, которая не требует переключения (еще одна не OO конструкция на мой взгляд) и карты:
types.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!!!
};
Types.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();
}
};
Недостающие части должны быть легко осуществимы.