第19章では State パターンを学びます。State とは、英語で「状態」を意味する単語です。
オブジェクト指向設計では、モノをクラスとして表現することが多くあります。State パターンとは、
モノではなく、「状態」をクラスとして表現するパターンです。
状態によって、動作のパターンが変わることがよくあります。
例えば、「機嫌のいい状態」「機嫌が悪い状態」の2つの状態があるお母さんにいくつか頼みごとをすることを考えます。
機嫌のいい状態のお母さんに「お小遣い頂戴」「おやつ頂戴」などのお願いをした場合、
「はいはい」といってお小遣いをくれたり、おやつを出してくれたりするでしょう。
しかし、機嫌の悪い状態のお母さんにこれらのお願いをしても聞き入れてくれないかもしれません。
お母さんは状態によって、振る舞いが変わるわけです。
State パターンとは、このような、状態の変化に応じて振る舞いが変わるような場合に威力を発揮するパターンです。
サンプルケースでは、あなたのクラスの女番長由実ちゃんに注目してみましょう。
由実ちゃんは男勝りで、いつも活発に活動しています。時々機嫌が悪くなって、
つっけんどんな会話をすることがありますが、とってもいい子です。
そんな由実ちゃんですが、恋をするととたんに女らしくなり、びっくりするほどおしとやかになります。
そして、恋が終わるとまた男勝りになるのです。
以下は、普段の由実ちゃんと、親友のみっこちゃんの会話を記録したものです。
| みっこちゃん |
: |
おはよう |
| 由実ちゃん |
: |
おっす! |
| みっこちゃん |
: |
今日も寒いねぇ |
| 由実ちゃん |
: |
走るか。 |
活発な女の子らしいですね。次に機嫌の悪い状態の由実ちゃんと、みっこちゃんの会話を見てみましょう。
| みっこちゃん |
: |
おはよう |
| 由実ちゃん |
: |
おお。 |
| みっこちゃん |
: |
今日も寒いねぇ |
| 由実ちゃん |
: |
めっちゃ寒いし、ももひき履いてきたわ。 |
うーん、まるでおっさんですね。
では、みっこちゃんからの問いかけに答えることができるように由実ちゃんクラスを実装してみましょう。
public class Yumichan {
/** 通常の由実ちゃんを表す */
private static final int STATE_ORDINARY = 0;
/** 機嫌の悪い由実ちゃんを表す */
private static final int STATE_IN_BAD_MOOD = 1;
/** 由実ちゃんの状態を表すプロパティ */
private int state = -1;
/**
* 由実ちゃんの状態を変更するメソッド
* @param state
*/
public void changeState(int state) {
this.state = state;
}
/**
* 朝のあいさつを返すメソッド
* @return
*/
public String morningGreet() {
if (state == STATE_ORDINARY) {
return "おっす!";
} else if (state == STATE_IN_BAD_MOOD) {
return "おお";
} else {
return "・・・";
}
}
/**
* 寒いときの防寒具を取得するメソッド
* @return
*/
public String getProtectionForCold() {
if (state == STATE_ORDINARY) {
return "走る";
} else if (state == STATE_IN_BAD_MOOD) {
return "ももひきをはく";
} else {
return "・・・";
}
}
}
さて、状態に応じて様々な返事を返す由実ちゃんクラスができました。 特に問題はないですね。
そんなある日、由実ちゃんは、同じクラスの松崎君に恋をしてしまいました。
みっこちゃんも初めてみたのですが、由実ちゃんは恋をすると、すっかり女の子らしくなるのでした。
恋に落ちた由実ちゃんと、先ほどの会話をしてみましょう。
| みっこちゃん |
: |
おはよう |
| 由実ちゃん |
: |
おはよう。 |
| みっこちゃん |
: |
今日も寒いねぇ |
| 由実ちゃん |
: |
寒いね、今日はピンクの毛糸パンツをはいてきたわ。 |
すっかり女の子らしくなりました。
では、由実ちゃんクラスに手を入れて状態を追加しましょう。この段階になって気づくわけです、
全部のメソッドの if 文を見直さなくてはならないことに。
このように、状態を追加する際に、全ての if 文の分岐を見直さなければならないような設計はあまり好ましいものではありません。
このように、「状態」に応じて、様々な振る舞いが変わるような場合、State パターンを利用するとうまくいきます。
State パターンでは、「状態」を表すクラスを用意し、この「状態」を入れ替え可能にしておきます。
サンプルケースであれば、まずは、「ご機嫌斜め状態」「普通の状態」が必要となります。
State の変更は、どのクラスが行っても良いのですが、今回のケースでは、内部のどこかから変更されるものとしてみます。
ソースコードを見てみましょう。
public class StatePatternYumichan {
/** 由実ちゃんの状態を表すプロパティ */
private State state = null;
/**
* 由実ちゃんの状態を変更するメソッド
* @param state
*/
private void changeState(State state) {
this.state = state;
}
/**
* 朝のあいさつを返すメソッド
* @return
*/
public String morningGreet() {
return this.state.morningGreet();
}
/**
* 寒いときの対策を取得するメソッド
* @return
*/
public String getProtectionForCold() {
return this.state.getProtectionForCold();
}
}
/**
* 由実ちゃんの状態を表すクラスが実装すべきインタフェース
*/
interface State {
/**
* 朝のあいさつを返すメソッドを定義する
* @return morningGreet
*/
public String morningGreet();
/**
* 寒いときの対策を返すメソッドを定義する
* @return
*/
public String getProtectionForCold();
}
class BadMoodState implements State {
/**
* 朝のあいさつです。機嫌の悪いときは、ぶっきらぼうに応えます。
*/
public String morningGreet() {
return "おお";
}
/**
* 冬の防寒方法です。機嫌の悪いときはももひきをはきます。
*/
public String getProtectionForCold() {
return "ももひき";
}
}
class OrdinaryState implements State {
/**
* 朝のあいさつです。通常は、男らしく応えます。
*/
public String morningGreet() {
return "おっす!";
}
/**
* 冬の防寒方法です。走るようです。
*/
public String getProtectionForCold() {
return "走る";
}
}
このようにしておくことで、「恋におちた状態」が増えたときに、全ての if 文の分岐を見直す必要がなくなりますね。由実ちゃんも恋がしやすくなるでしょう。