在iOS中等待用户交互的设计模式?

时间:2013-02-21 06:04:06

标签: ios objective-c user-input state-machine

我正在为iOS开发BlackJack游戏。跟踪当前状态和需要完成的工作变得困难。例如,我有一个跟踪当前游戏的C ++类:

class Game {
  queue<Player> playerQueue;
  void hit();
  void stand();
}

目前我正在使用事件(方法A)来实现它:

- (void)hitButtonPress:(id)sender {
  game->hit();
}

void Game::hit() {
  dealCard(playerQueue.top());
}

void Game::stand() {
  playerQueue.pop();
  goToNextPlayersTurn();
}

随着越来越多的选项被添加到游戏中,为每个选项创建事件变得乏味且难以跟踪。

我想实现它的另一种方式就是这样(方法B):

void Game::playersTurn(Player *player) {
  dealCards(player);
  while (true) {
    string choice = waitForUserChoice();
    if (choice == "stand") break;
    if (choice == "hit")
      dealCard(player);
    // etc.
  }
  playerQueue.pop();
  goToNextPlayersTurn();
}

其中waitForUserChoice是一个允许用户与UIViewController交互的特殊函数,一旦用户按下按钮,则只有控件返回playersTurn函数。换句话说,它会暂停程序直到用户点击按钮。

使用方法A,我需要在每次需要用户交互时拆分我的功能。方法B让一切都保持控制。 基本上方法A和B之间的区别如下:

A:

function A() {
  initialize();
  // now wait for user interaction by waiting for a call to CompleteA
}

function CompleteA() {
  finalize();
}

B:

function B() {
  initialize();
  waitForUserInteraction();
  finalize();
}

注意B如何使代码更有条理。有没有办法用Objective-C做到这一点?或者是否有一种我没有提到的不同方法呢?

我能想到的第三个选择是使用有限状态机。我听过一些关于它们的内容,但我确定在这种情况下是否能帮助我。

我的问题的推荐设计模式是什么?

1 个答案:

答案 0 :(得分:1)

我理解你遇到的两难困境。当我第一次启动iOS时,我很难绕过放弃对操作系统的控制。

一般情况下iOS会鼓励你使用方法A.通常你在ViewController中有变量,它们在方法A()中设置,然后在CompleteA()中检查它们以验证A()是否先运行等。

关于有限状态机的问题,我认为它可以帮助您解决问题。我在iOS编写的第一件事是FSM(这是非常糟糕的代码)但是你可以看看这里(靠近FlipsideViewController.m的底部:

https://github.com/esromneb/ios-finite-state-machine

一般的想法是你将它放在@interface块中的.h文件中

static int state = 0;
static int running = 0;

在你的.m中你有这个:

- (void) tick {

    switch (state) {
        case 0:
            //this case only runs once for the fsm, so setup one time initializations

            // next state
            state = 1;

            break;
        case 1:
            navBarStatus.topItem.title = @"Connecting...";
            state = 2;
            break;
        case 2:
            // if something happend we move on, if not we wait in the connecting stage
            if( something )
                state = 3;
            else
                state = 1;
            break;
        case 3:
            // respond to something

            // next state
            state = 4;
            break;
        case 4:
            // wait for user interaction
            navBarStatus.topItem.title = @"Press a button!";
            state = 4;

            globalCommand = userInput;

            // if user did something
            if( globalCommand != 0 )
            {
                // go to state to consume user interaction
                state = 5;  
            }

            break;

        case 5:
            if( globalCommand == 6 )
            {
                // respond to command #6
            }
            if( globalCommand == 7 )
            {
                // respond to command #7
                }

                        // go back and wait for user input
                        state = 4;
            break;

        default:
            state = 0;
            break;
    }

    if( running )
    {
        [self performSelector:@selector(tick) withObject:nil afterDelay:0.1];
    }
}

在这个例子中(从github上的一个修改),globalCommand是一个表示用户输入的int。如果globalCommand为0,则FSM仅在状态4中旋转,直到globalCommand为非零。

要启动FSM,只需将running设置为1并从viewController调用[self tick]。 FSM将每0.1秒“勾选”一次,直到运行设置为0。

在我最初的FSM设计中,我不得不从运行它自己的软件的Windows计算机响应用户输入和网络输入。在我的设计中,Windows PC也运行了类似但不同的FSM。对于这种设计,我使用NSMutuableArray构建了两个命令的FIFO队列对象。用户交互和网络数据包会将命令排入队列,而FSM会将项目出列并对其进行响应。我最终使用https://github.com/esromneb/ios-queue-object作为队列。

如果您需要任何澄清,请发表评论。