Help us understand the problem. What is going on with this article?

Android でステートマシンを実現するメモ(Enum版)

More than 3 years have passed since last update.

State パターン(GoF)を適用したくなるケースとしては、同じメソッドでも 状態 によって処理の内容が変わる場合、かつ、if 文等による分岐処理を書くと複雑になりコードの見通しが悪くなってしまう場合。または 状態 に関連するコードを一箇所に集めて見通しを良くしたい場合。

例:通信状態の場合はローディングを表示したい、ボタン押下を無効にしたい等

今回は次のような 状態遷移 の簡単なステートマシンを実現したいとします。

  [ Idle ]

      ↓ doApiCall()

  [ Processing ]

これを Java の Enum で表現すると次のようになります。今回は Enum の定義を Activity のコードの中に内包します。

コード量が多くなる場合は Enum 用のファイルを別に作ってもよいかと思います。また状態を Enum でなくクラスで表現する方法もあります。

SampleActivity
public class SampleActivity extends AppCompatActivity {

    // ...

    // 普通に Activity のロジックを書く。今回は略

    // ...

    private State state = State.Idle; // 初期状態

    // 状態を変更するメソッド
    private void changeState(State nextState) {
        state.exit(this);
        state = nextState;
        state.enter(this);
    }

    // ステートマシン。状態別に処理させたい内容を定義
    private enum State {
        Idle {
            @Override
            public void doApiCall(SampleActivity parent) {
                parent.changeState(Processing); // 状態を Processing へ変更

                // 何か通信処理 ..
            }
        },
        Processing {
            // 今回は略してますが enter() でローディング表示など書く
        },
        ;
        public void enter(SampleActivity parent) {} // 状態に入った際の処理を何か定義したい場合はオーバーライドする
        public void doApiCall(SampleActivity parent) {} // 中身が空なのでオーバーライドしなければ呼びだされても何も処理されない
        public void exit(SampleActivity parent) {} // 状態から出た際の処理を何か定義したい場合はオーバーライドする
    }
}

Enum の中で直に SampleActivity を参照していますが Interface を定義して参照しても良いかと思います。

あとは Actvity のどこかで state.doApiCall(this) とすれば通信処理が始まり、状態は Processing に遷移します。Processing 状態では doApiCall() を受け付けません(オーバーライドしていないので)。

今回は省略していますが、通信処理が終わったらまた状態を Idle に戻せばよいかと思います。

State パターンでは状態遷移をどこから指示するか問題があり、上記の例では状態遷移メソッド changeState()Enum の定義の中から呼び出していますが、状況に応じて Activity 側から changeState(State.Processing) のように呼び出して遷移させても構いません。

ほか

  • 状態数が少ない場合やそもそも複雑でない場合は使わない方がよいかもしれない。
  • if 文で分岐している処理やフラグで分岐する処理は何でもこれで表現したくなる気分になるが、それを「状態」として昇格するか吟味すること。
    • フラグはフラグで分かりやすいケースは多い。
  • Rx のストリームの中で分岐処理を書きたい場合に有用かも(未検証)。

参考にした記事・書籍

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away