Фабричный шаблон: что делать, если фабричный класс слишком большой?

Я использую Entity-Base-Компоненты системы.

У меня много типа stationary objects например

  1. Стена = блоки
  2. Fire Turret = блоки + стрелок
  3. Водяная турель = блоки + стрелок
  4. Бункер = блоки + спавнер

Здесь находится фабрика stationary objects : —

class StationaryObject{
enum typeOfObject_enum{WALL,FIRE_TURRET, ....};
Entity* create(typeOfObject_enum theType){ //it is enum
switch(theType){
case WALL: ... create some mesh, some physic body  ....
case FIRE_TURRET: .... create some mesh, some physic body+ unique logic 20 lines ....
....
}
}
}

Это работает действительно хорошо.

Вопрос:
Теперь я хочу создать 100 типов Stationary objectsГде я должен хранить это?
Храните их всех в классе StationaryObjectсделает класс слишком большим (?).

Обратите внимание, что в каждом типе объекта есть крошечная, но уникальная логика.

2

Решение

Вы можете создать карту из typeOfObject_enum для каждой фабрики объектов, а затем вы можете зарегистрировать фабрики на карте, как вы хотите.

Каждый объект фабрики может быть чем-то вродеstd::function<std::unique_ptr<Entity>()>:

enum class StationaryObjectType{WALL, FIRE_TURRET, WATER_TURRET};
const size_t STATIONARY_OBJECT_TYPE_COUNT = 3;
using EntityFactory = std::function<std::unique_ptr<Entity>()>;

class StationaryObjectFactory {
std::array<EntityFactory, STATIONARY_OBJECT_TYPE_COUNT> factory_map;
public:
void registerFactory(StationaryObjectType type, EntityFactory factory){
factory_map[static_cast<size_t>(type)] = std::move(factory);
}
std::unique_ptr<Entity> create(StationaryObjectType type){
auto factory = factory_map[static_cast<size_t>(type)];
if (!factory)
return nullptr;
return factory();
}
};

int main() {
StationaryObjectFactory factory;

// Register lambdas as the factory objects
factory.registerFactory(StationaryObjectType::WALL, []{
return std::make_unique<Wall>();
});
factory.registerFactory(StationaryObjectType::FIRE_TURRET, []{
return std::make_unique<FireTurret>();
});

auto wall = factory.create(StationaryObjectType::WALL);
auto fire_turret = factory.create(StationaryObjectType::FIRE_TURRET);
auto water_turret = factory.create(StationaryObjectType::WATER_TURRET);

assert(wall != nullptr);
assert(fire_turret != nullptr);
assert(water_turret == nullptr);  // No WATER_TURRET factory registered
}

Живая демоверсия.

Или, если хотите, вы можете использовать реализации абстрактного класса фабрики:

class EntityFactory {
public:
virtual ~EntityFactory(){}
virtual std::unique_ptr<Entity> operator()() = 0;
};

class WallFactory : public EntityFactory {
public:
std::unique_ptr<Entity> operator()() override {
return std::make_unique<Wall>();
}
};

class FireTurretFactory : public EntityFactory {
public:
std::unique_ptr<Entity> operator()() override {
return std::make_unique<FireTurret>();
}
};

Живая демоверсия.

4

Другие решения

Я не использовал C ++, но похоже, что вы можете смешивать шаблоны компоновщика и фабрики, чтобы получить то, что вы хотите,

class StationaryObject{
Entity create(typeOfObject_enum theType){ //it is enum
switch(theType){
case WALL:
return WallBuilder.Build();
case FIRE_TURRET:
return FireTurrentBuilder.Build();
....
}
}
}

Вы можете оптимизировать его, добавив базовый класс BaseBuilder, в котором у вас есть общая логика для разных сущностей.

class BaseBuilder<T> {
Mesh CreateMesh(){...}

....

T Build();
}

class WallBuilder : BaseBuilder<Wall> {
Wall Build(){
}
}

При таком подходе вы можете использовать отображение между enum и builder и избавиться от оператора case

1