マリオでデザインパターン その1 【Strategyパターン】
StateパターンじゃなくてStrategyパターンのようなのでタイトル変更
マリオには以下のアクションがあります。
- ジャンプ
- パンチ
さらに以下の状態があります
それぞれの状態では以下のアクションを行います
Stateパターンを使わない例
MarioStateEnum
public enum MarioState { Default, Big, Fire; }
Marioクラス
public class Mario { public MarioState state = MarioState.Default; public void jump(){ switch(state){ case Big: System.out.println("デカジャンプ"); break; case Fire: System.out.println("ファイアジャンプ"); break; case Default: System.out.println("デフォジャンプ"); break; } } public void punch(){ switch(state){ case Big: System.out.println("デカパンチ"); break; case Fire: System.out.println("ファイアパンチ"); break; case Default: System.out.println("デフォパンチ"); break; } } }
クライアント
public class ClientMain { public static void main(String[] args) { Mario mario = new Mario(); action(mario); System.out.println(); System.out.println("--マリオはスーパーきのこを食べた-"); mario.state = MarioState.Big; action(mario); System.out.println(); System.out.println("--マリオはファイアフラワーを食べた-"); mario.state = MarioState.Fire; action(mario); } public static void action(Mario mario){ //ジャンプする System.out.println("[マリオはジャンプした]"); mario.jump(); //パンチする System.out.println("[マリオはパンチした]"); mario.punch(); } }
実行結果
[マリオはジャンプした] デフォジャンプ [マリオはパンチした] デフォパンチ --マリオはスーパーきのこを食べた- [マリオはジャンプした] デカジャンプ [マリオはパンチした] デカパンチ --マリオはファイアフラワーを食べた- [マリオはジャンプした] ファイアジャンプ [マリオはパンチした] ファイアパンチ
このパターンだと状態が増えるたびにswitch文とEnumクラスを書き直さなくてはいけません。
これをStateパターンを使うとソースを書き直さずにすみます。
Stateパターンを使った例
それぞれの状態での振る舞いの内容は違いますが、jump、puchという振る舞いは同じです。
Stateパターンは、「状態」をクラスとして表現し、振る舞いはサブクラスに任せます。
Stateパターンは、以下の構成です。
- State
状態を表します。
状態ごとに異なる抽象化された振る舞いを定義します。
- ConcreteState
具体的な状態を表します。
Stateインターフェース(クラス)を実装します。
抽象メソッドをオーバーライドし、具体的な振る舞いを実装します。
- Context
状態オブジェクトを管理し、振る舞いを実行する。
【State役の作成】
State役となる状態のインターフェースを作成し、振る舞いの抽象メソッドを追加する。
ここでは、
- jumpメソッド
- punchメソッド
を定義します。
MarioStateインターフェース
public interface MarioState { public void punch(); public void jump(); }
【ConcreteState役の作成】
ConcreteState役となる、Stateインターフェースを実装したデフォルトマリオクラスを作成する。
jump、punchメソッドをオーバーライドする
DefaultMarioStateImplクラス
public class DefaultMarioStateImpl implements MarioState { @Override public void jump() { System.out.println("デフォジャンプ"); } @Override public void punch() { System.out.println("デフォパンチ"); } }
同様に、デカマリオ、ファイアマリオを作成する
DekaMarioStateImplクラス
public class DekaMarioStateImpl implements MarioState { @Override public void jump() { System.out.println("デカジャンプ"); } @Override public void punch() { System.out.println("デカパンチ"); } }
FireMarioStateImplクラス
public class FireMarioStateImpl implements MarioState { @Override public void jump() { System.out.println("ファイアジャンプ"); } @Override public void punch() { System.out.println("ファイアパンチ"); } }
【Context役の作成】
Context役となる、MarioStateインタフェースを含んだContextMarioクラスを作成する
MarioStateインターフェースを引数にもった
- doJumpメソッド
- duPunchメソッド
を実装する。
ポリモフィズムを使うことによってマリオの状態を意識する必要がなくなる。
switch文がなくなってハッピー。
public class ContextMario { private MarioState marioState; public ContextMario(){ this.marioState = new DefaultMarioStateImpl(); } public void setMarioState(MarioState marioState) { this.marioState = marioState; } public void doJump() { this.marioState.jump(); } public void doPunch() { this.marioState.punch(); } }
Clientクラス
public class ClientMain { public static void main(String[] args) { ContextMario mario = new ContextMario(); action(mario); System.out.println(); System.out.println("--マリオはスーパーきのこを食べた-"); mario.setMarioState(new DekaMarioStateImpl()); action(mario); System.out.println(); System.out.println("--マリオはファイアフラワーを食べた-"); mario.setMarioState(new FireMarioStateImpl()); action(mario); } public static void action(ContextMario mario){ System.out.println("[マリオはジャンプした]"); mario.doJump(); System.out.println("[マリオはパンチした]"); mario.doPunch(); } }
実行結果
[マリオはジャンプした] デフォジャンプ [マリオはパンチした] デフォパンチ --マリオはスーパーきのこを食べた- [マリオはジャンプした] デカジャンプ [マリオはパンチした] デカパンチ --マリオはファイアフラワーを食べた- [マリオはジャンプした] ファイアジャンプ [マリオはパンチした] ファイアパンチ