假设我们正在制作一个两人纸牌游戏,并且我们有称为Game,Player和Card的类。游戏包含指向两个玩家的指针,并为玩家提供界面。牌手包括牌手的生命值,其魔力以及其矢量牌。卡是一个抽象类。每张卡都需要花费魔法才能使用。
问题在于,每张纸牌在被使用时都可以通过多种方式改变游戏状态。例如,我们可以有一张牌,使牌手的生命值加倍,一张牌使敌方玩家中毒两回合,一张牌会破坏棋盘上的所有小兵,一张牌会为棋盘上已经存在的每个小兵创建副本,等等。可能性真的是无止境的。我能看到的唯一解决方案是在每张纸牌中都有一个指向游戏的指针,但这似乎不太优雅。
还有其他解决方案吗?
答案 0 :(得分:6)
您的基本想法是正确的,但是您应该进一步增强它,以保持较高的抽象和封装水平。
因此,在您的示例中,假设您有一个Card
类,它是现有类的实例。
第一件事是将卡的效果与卡本身分开,例如:
class CardEffect {
// TODO ...
};
class Card {
private:
const CardEffect* effect;
};
因此,现在卡本身不需要了解游戏。但是,让我们更深入地了解这一点:CardEffect
不需要知道Game
类的每个细节,它需要做的就是将效果应用到游戏中。这可以通过为效果提供一个单独的接口来完成,该接口仅公开应用该效果所需的内容,因此类似这样。
class GameInterface {
public:
virtual const std::vector<Player*>& getPlayers() = 0;
virtual damagePlayer(Player* player, int amount) = 0;
virtual applyPeriodicDamage(Player* player, int turns, int amount) = 0
..
};
class CardEffect {
virtual applyEffect(GameInterface* interface) = 0;
};
class Card {
private:
const CardEffect* effect;
};
现在这只是一个示例,由于每种特定情况都不相同且有不同的要求,因此没有针对您的问题的最终解决方案,这只是为您提供了一个基本思路,使您可以尝试在保持代码优雅的同时将其封装起来描述性强,易于管理。
答案 1 :(得分:2)
只要您有交叉依赖项,例如:
class Card
{
Game* game;
void f() {
game->method1();
}
};
class Game
{
std::vector<Card> cards;
public:
void method1();
};
其中一个依赖项应实现如下接口:
class IGame
{
public:
virtual void method1()=0;
};
class Card
{
IGame* game;
void f() {
game->method1();
}
};
class Game : public IGame
{
std::vector<Card> cards;
public:
virtual void method1();
};
而且您再也看不到交叉依赖性。