我正在为自己和娱乐而设计一款小游戏。游戏的真实身份与我的实际问题无关紧要,假设它是Mastermind游戏(它实际上是:)
我在这里的真正目标是拥有一个IPlayer
接口(interface),该接口(interface)可用于任何播放器:计算机或人工,控制台或gui,本地或网络。我还打算有一个GameController,它将只处理两个IPlayer
。
IPlayer接口(interface)看起来像这样:
class IPlayer
{
public:
//dtor
virtual ~IPlayer()
{
}
//call this function before the game starts. In subclasses,
//the overriders can, for example, generate and store the combination.
virtual void PrepareForNewGame() = 0;
//make the current guess
virtual Combination MakeAGuess() = 0;
//return false if lie is detected.
virtual bool ProcessResult(Combination const &, Result const &) = 0;
//Answer to opponent's guess
virtual Result AnswerToOpponentsGuess(Combination const&) = 0;
};
GameController类将执行以下操作:
IPlayer* pPlayer1 = PlayerFactory::CreateHumanPlayer();
IPlayer* pPlayer1 = PlayerFactory::CreateCPUPlayer();
pPlayer1->PrepareForNewGame();
pPlayer2->PrepareForNewGame();
while(no_winner)
{
Guess g = pPlayer1->MakeAguess();
Result r = pPlayer2->AnswerToOpponentsGuess(g);
bool player2HasLied = ! pPlayer1->ProcessResult(g, r);
etc.
etc.
}
通过这种设计,我愿意使GameController类不可变,也就是说,我只将其中的游戏规则填充进去,而别无其他,因此,既然游戏本身已经建立,则该类不应更改。对于主机游戏,该设计将完美运行。我将使用
HumanPlayer
(在其MakeAGuess
方法中会从标准输入中读取Combination
)和CPUPlayer
(将以某种方式随机生成)等。现在,这是我的问题:
IPlayer
接口(interface)以及GameController
类在本质上是同步的。我无法想象当GameController
的MakeAGuess
方法必须等待例如某些鼠标移动和点击时,如何用相同的GUIHumanPlayer
实现游戏的GUI变体。当然,我可以启动一个新线程,该线程将等待用户输入,而主线程将阻塞,以模仿同步IO,但这在某种程度上使我感到厌恶。或者,我也可以将 Controller 和播放器设计为异步的。在这种情况下,对于主机游戏,我将不得不模仿异步性,这似乎比第一个版本容易。您能否评论我的设计以及对选择同步或异步设计的担忧?另外,我觉得我对玩家类比GameController类承担更多责任。等等
提前非常感谢您。
附言我不喜欢我的问题的标题。随时编辑它:)
最佳答案
可以考虑为IPlayer
对象引入一个观察者类,而不是使用各种IPlayer
方法的返回值:
class IPlayerObserver
{
public:
virtual ~IPlayerObserver() { }
virtual void guessMade( Combination c ) = 0;
// ...
};
class IPlayer
{
public:
virtual ~IPlayer() { }
virtual void setObserver( IPlayerObserver *observer ) = 0;
// ...
};
然后,
IPlayer
的方法应调用已安装的IPlayerObserver
的相应方法,而不是返回值,如:void HumanPlayer::makeAGuess() {
// get input from human
Combination c;
c = ...;
m_observer->guessMade( c );
}
然后,您的GameController类可以实现
IPlayerObserver
,以便在玩家进行一些有趣的操作(例如进行猜测)时得到通知。通过这种设计,如果所有
IPlayer
方法都是异步的,那就很好了。实际上,这是可以预期的-它们都返回void
!。您的游戏 Controller 会在 Activity 的玩家上调用makeAGuess
(这可能会立即计算结果,或者可能会为多人游戏提供一些网络IO,或者会等待GUI进行操作),并且只要玩家做出选择,游戏 Controller 可以放心guessMade
方法将被调用。此外,玩家对象仍然对游戏 Controller 一无所知。他们只是在处理一个不透明的“IPlayerObserver”接口(interface)。