Я использую Entity-Base-Компоненты системы.
У меня много типа stationary objects
например
- Стена = блоки
- Fire Turret = блоки + стрелок
- Водяная турель = блоки + стрелок
- Бункер = блоки + спавнер
Здесь находится фабрика 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
сделает класс слишком большим (?).
Обратите внимание, что в каждом типе объекта есть крошечная, но уникальная логика.
Вы можете создать карту из 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>();
}
};
Я не использовал 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