c++ - 我在这里违反了 OOP 设计指南吗?几个有趣的设计泡菜

标签 c++ oop

我正在为我正在创建的游戏设计一个新的能量提升系统。这是一个横向卷轴,能量提升显示为圆形物体,玩家必须触摸/穿过它们才能获得能量。然后启动电源,并在几秒钟后自行停用。每个加电都有自己定义的持续时间。为简单起见,每 X 秒产生一次能量提升(放置在屏幕上)。

我创建了一个 PowerUpManager,它是一个单例,它的工作是决定何时创建新的 Prop ,然后将它们放置在何处。

然后我创建了 Powerup 基类,并为每个新的 Powerup 创建了一个从该基类继承的类。每个 Power-up 都可以处于三种状态之一:禁用、放置在屏幕上以及被玩家拾取。如果玩家没有拿起 Prop 而是继续前进, Prop 将退出屏幕并从放置状态返回到禁用状态,以便再次放置。

(我)提出的要求之一是,当我编写一个新的 Power up 类时,代码更改应该最少。我能做的最好的就是一段代码:PowerUpManager 的构造函数,您必须在其中将新的 Prop 添加到包含所有 Prop 的容器中:

PowerupManager::PowerupManager()
{
    available = {
        new PowerupSpeed(),
        new PowerupAltWeapon(),
        ...
    };
}

PowerUpManager,更详细(问题来了!): 持有指向称为可用的 PowerUp(基类)的指针 vector 。这是初始容器,其中包含游戏中每个能量提升的拷贝。 为了处理不同的状态,它有几个列表:一个包含指向当前放置的电源的指针,另一个列表包含指向当前事件的电源的指针。 它还有一个在每个游戏节拍都被调用的方法,该方法决定是否以及在何处放置新的 Prop 并清理未拾取的 Prop 。最后,它有一个方法,当玩家遇到加电时调用,激活加电(将其从放置列表移动到事件列表,并调用加电的激活方法)。

最后,一旦你理解了全貌,问题: 我需要一种方法让客户端代码询问当前是否有特定的能量提升。例如:玩家拥有一件武器,但有一个 Prop 可以暂时取代该武器。在轮询输入并识别玩家想要开火的地方,我需要调用正确的开火方法 - 替代武器加电开火方法,而不是常规武器开火方法。

我想了一会儿这个特殊的需求,想出了这个:

template <typename T>
T* isActivated() // Returns a pointer to the derived Powerup if it exists in the activated list, or nullptr if it doesn't
{
    for(Powerup *i : active) // Active is a list of currently active power ups
    {
        T *result = dynamic_cast<T*>(i);

        if(result)
            return result;
    }

    return nullptr;
}

因此客户端代码如下所示:

PowerUpAltWeapon *weapon = powerUpManager->isActivated<PowerUpAltWeapon>();
if(weapon)
    ...

我认为该解决方案优雅而简洁,但本质上它是试图将基类型转换为派生类型。如果这不起作用,则尝试下一个派生类型......一长串 if/else if,它只是伪装在一个循环中。这是否违反了我刚才描述的准则?不在 if/else if 的长链中将基类型转换为它的所有派生类型,直到你被击中?还有其他解决方案吗?

第二个问题是:有没有办法摆脱在 PowerupManager 构造函数中构造所有不同电源的需要?如果您想引入新的能量提升,这是目前唯一需要进行更改的地方。如果我能摆脱它,那会很有趣...

最佳答案

这是基于您的设计,但如果是我,我会为每个 PowerUp 选择一个 ID,并在客户端中选择一组 ID,每次用户拥有 PowerUp 时,该 ID 都会添加到其集合中,并且.. . 你知道其余的。使用这种技术,我可以快速查找每个 PowerUp 并避免 dynamic_cast:

std::set<PowerUp::ID> my_powerUps;
template< class T > bool isActivated() {
    return my_powerUps.find( T::id() ) != my_powerUps.end();
}

关于你的第二个问题,我有一个类似的程序加载一些插件而不是 PowerUp,我有一个纯虚拟基类,它包含该插件所需的所有方法并在共享中实现它模块,然后在启动时我从特定文件夹加载它们。例如,每个共享模块都包含一个 create_object ,它返回一个 plugin* (在你的情况下当然是 PowerUp* )然后我迭代文件夹,加载模块并调用 create_object 从它们创建我的插件并将它们注册到我的 plugin_manager

关于c++ - 我在这里违反了 OOP 设计指南吗?几个有趣的设计泡菜,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12881156/

相关文章:

c++ - 不带下划线的用户定义文字

c++ - 在 C++ 中初始化 boolean vector 的标准方法是什么

angular - typescript - 什么是更好的 : Get/Set properties

c# - 在 C# 中实现组合和聚合?

arrays - 支持完整方法链的 Ruby Extended Array 类

c++ - std::thread 和异常处理

c++ - 内存映射文件如何与写入文件的另一个进程同步?

C++将wchar_t反斜杠插入字符串

c# - 如何创建二级枚举

java - Java 错误 :cannot find Symbol 中的多态性