Я создаю небольшую игру для собственного удовольствия и тренировок. Реальная идентичность игры не имеет никакого отношения к моему актуальному вопросу, предположим, что это игра Mastermind (которая на самом деле :)
Моя настоящая цель здесь - иметь интерфейс IPlayer
, который будет использоваться для любого игрока: компьютера или человека, консоли или графического интерфейса, локального или сетевого. Я также намереваюсь иметь GameController, который будет иметь дело только с двумя IPlayer
s.
интерфейс 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
синхронны по своей природе. Я не представляю, как бы я реализовал вариант игры с графическим интерфейсом с тем же GameController
, когда MakeAGuess
метод GUIHumanPlayer
должен был бы ждать, например, некоторых движений мыши и щелчков. Конечно, я мог бы запустить новый поток, который бы ожидал пользовательского ввода, в то время как основной поток блокировал бы, чтобы имитировать синхронный ввод-вывод, но почему-то эта идея мне противна. Или, в качестве альтернативы, я мог бы спроектировать контроллер и плеер как асинхронные. В этом случае для консольной игры мне пришлось бы имитировать асинхронность, которая кажется проще, чем в первой версии.
Не могли бы вы прокомментировать мой дизайн и мои опасения по поводу выбора синхронного или асинхронного дизайна? Кроме того, я чувствую, что возлагаю больше ответственности на класс игрока, чем на класс GameController. И т. Д.
Большое спасибо заранее.
P.S. Мне не нравится название моего вопроса. Не стесняйтесь редактировать это :)