【问题标题】:Factory pattern : What to do when factory class is too big?工厂模式:工厂类太大怎么办?
【发布时间】:2016-09-01 08:28:32
【问题描述】:

我正在使用实体基础组件系统。

我有很多类型的stationary objects,例如

  1. 墙=块
  2. 火炮塔 = 方块 + 射手
  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会使类太大(?)。

请注意,每种类型的对象都有微小但独特的逻辑。

【问题讨论】:

  • 我强烈建议您切换到最新的 c++ 标准。使用enum class而不是enum,返回std::unique_ptr<Entity>而不是Entity*
  • 为什么还需要工厂?
  • 为什么它比std::make_unique<WallType>更好?我个人认为工厂真的解决不了问题,它来自于 Java 的邪恶之地。在不知不觉中,你的代码被数百个工厂、构建器、抽象类和其他邪恶的、不必要的垃圾炸毁了。
  • "WallType 不是 C++ 意义上的类型,它是游戏对象的类型。"好吧,听起来它也应该是 C++ 类型。在这里,我解决了你的问题
  • @DavidHaim 我同意你的观点,在这种情况下工厂没有意义;但是,说“工厂真的什么都解决不了”也太过分了。工厂让您控制在运行时实例化的实现。工厂允许您放弃在单个类中使用什么实现的所有决策。工厂允许您根据运行时参数连接对象...我可以继续,但您明白了。

标签: c++ design-patterns factory-pattern component-based


【解决方案1】:

您可以创建从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
}

Live demo.

或者,如果您愿意,可以使用抽象工厂类的实现:

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>();
    }
};

Live demo.

【讨论】:

  • 鉴于它是一个没有特定值的枚举,因此更喜欢 std::array 而不是 std::unordered_map。
【解决方案2】:

我没有用过 C++,但听起来你可以混合 builder 和 factory 模式来得到你想要的,

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(){
   }
}

通过这种方法,如果您愿意,可以使用枚举和构建器之间的映射,并摆脱 case 语句

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多