这部分的内容是之前答应过之后再说的状态模式,这个模式在 TapController 用到。与 MVC 和 DAOs 不同的是,我不会把状态模式归为 android 程序架构必须实现的部分。然而,它的确非常有用。因为我在 TapController 中用它来处理消息,所以还是有必要说一下。

##状态模式

在之前的系列中我没有提到它是怎样在 android 里工作的。但是在这部分,我们将会更深入一些来探讨它。状态模式是一个面向对象的模式,目的是可以动态切换对象的行为。还记得对象具有属性和行为吗?你会觉得这听起来像是策略模式?对,你说得没错。这里不同的地方就是模式的意图。状态模式是根据属性的不同而改变其行为的。客户端甚至不知道调用的对象已经发生转换。那么是怎么做到动态切换而接口不变的?我们需要把行为封装到对象内部,然后让他妈共享一套接口。接下来我们聊聊具体的例子,这个例子就是 Tap Counter 工程了。

我们要看的连个对象叫做 UnlockedStateLockedState。当 CounterVo 设置为被锁,这意味着我们不希望属性改变。例如,如果用户点击了增加按钮,如果状态是锁住得话,值将不会增加。但是如果不是锁住的状态,用户点击将会使值增加。这是基于 CounterVo.locked 的两种不同行为。

当 TapController.handleMessage 被触发,它就会把消息代理给其中一种状态。这里使用了 Controller.messageState 来确定用哪一种转台。让我们看看代码吧。

:::java
@Override
public boolean handleMessage(int what) {
    return messageState.handleMessage(what);
}

这的确非常简单,但是 messageState 的状态是如何改变的呢?

两个状态之间是彼此知道对方的。当 UnLockedState 接到 MESSAGE_UPDATE_LOCK 这个消息时,它就会把 messageState 的引用改为 LockedState 。然后当 MESSAGE_UPDATE_LOCK 消息被 LockedState 处理时它会更细 messageState 的引用到 UnlockedState。我觉得我们看看代码会更好理解一些。

:::java
package com.musselwhizzle.tapcounter.controllers;
 
public class UnlockedState extends TapState {
 
    // ... properties
 
    public UnlockedState(TapController controller) {
        super(controller);
    }
 
    @Override
    public boolean handleMessage(int what) {
        switch(what) {
            case TapController.MESSAGE_INCREMENT_COUNT:
                moveCount(1);
                return true;
            case TapController.MESSAGE_DECREMENT_COUNT:
                moveCount(-1);
                return true;
            case TapController.MESSAGE_RESET_COUNT:
                model.setCount(0);
                return true;
            default:
                return super.handleMessage(what);
        }
 
    }
 
    @Override
    public boolean handleMessage(int what, Object data) {
        switch(what) {
            case TapController.MESSAGE_UPDATE_LOCK:
                updateLock((Boolean)data);
                return true;
            case TapController.MESSAGE_UPDATE_LABEL:
                updateLabel((String)data);
                return true;
            case TapController.MESSAGE_KEY_EVENT:
                return handleKeyEvent((KeyEvent)data);
            default:
                return super.handleMessage(what, data);
        }
    }
 
    private boolean handleKeyEvent(KeyEvent event) {
        // .. handles the key event
    }
 
    private void moveCount(int amount) {
        model.setCount(model.getCount()+amount);
    }
 
    private void updateLock(boolean lock) {
        model.setLocked(lock);
        controller.setMessageState(new LockedState(controller));
    }
 
    private void updateLabel(String label) {
        model.setLabel(label);
    }
}

UnLockedState 通知控制器去改变状态为 LockedState 的代码如下。

:::java
controller.setMessageState(new LockedState(controller));

现在我们可以改变状态和知道代理发生了什么事了。让我们来看看两种不同状态是如何处理 TapController.MESSAGE_INCREMENT_COUNT 的。在 UnlockedState 看到的代码就是你想的那样

:::java
@Override
public boolean handleMessage(int what) {
    switch(what) {
        case TapController.MESSAGE_INCREMENT_COUNT:
    model.setCount(model.getCount()+1);
    return true;
    }
}

然后看看 LockedState

:::java
@Override
public boolean handleMessage(int what) {
    switch(what) {
        case TapController.MESSAGE_INCREMENT_COUNT:
    return true;
    }
}

什么也没做,只是返回了 true 表示处理了这个消息。

那么为什么你要做这些工作?你不可以使用 if-else 语句来判断 CounterVo.locked 来完成这些工作吗?

我们可以这么做,但当你使用 if-else 来管理状态,一旦状态变得复杂或者增多,你的 if-else 将会变得又长又臭。你的代码会变得很难维护,如果想加入新特性很可能只有重构了。使用状态模式你可以很方便地为新状态增加新的行为,而且遵循 开放/封闭 原则。当然,在我们的案例中,它有可能有杀鸡用牛刀的感觉。但无论如何,我只想创建一个简单的使用真实解决方案和架构的真实工程。