【发布时间】:2011-03-07 12:31:17
【问题描述】:
为了我自己的乐趣和训练,我正在设计一个小游戏。游戏的真实身份与我的实际问题无关,假设它是Mastermind 游戏(实际上是:)
我在这里的真正目标是有一个界面IPlayer,它将用于任何玩家:计算机或人类、控制台或图形用户界面、本地或网络。我还打算有一个 GameController,它只处理两个 IPlayers。
IPlayer 界面看起来像这样:
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 接口和GameController 类本质上是同步的。我无法想象当GUIHumanPlayer 的MakeAGuess 方法必须等待例如一些鼠标移动和点击时,我将如何使用相同的GameController 实现游戏的GUI 变体。当然,我可以启动一个等待用户输入的新线程,而主线程会阻塞,以模仿同步 IO,但不知何故,这个想法让我感到厌恶。或者,或者,我可以将控制器和播放器都设计为异步的。在这种情况下,对于主机游戏,我将不得不模仿异步,这似乎比第一个版本更容易。
您能否评论一下我的设计以及我对选择同步或异步设计的担忧?另外,我觉得我把更多的责任放在了玩家类上,而不是 GameController 类。等等等等。
提前非常感谢您。
附:我不喜欢我的问题的标题。随意编辑它:)
【问题讨论】:
-
我喜欢这个标题 :-)
-
我删除了 C# 标签,因为这个问题附近没有 C#。
-
由于语言无关紧要,我也希望得到 C# 专家的意见
-
然后在其上放置一个与语言无关的标签,而不是特定的语言标签。语言标签用于针对特定语言的问题。为什么不在阳光下用每种 OOP 语言标记它?
-
一般来说,不鼓励使用标签来吸引更多的受众。标记用于对问题进行分类。
标签: c++ oop design-patterns language-agnostic