前のレッスンの状態モード学習では、キャンディマシンゲームを実装してみましょう。キャンディマシンには、次の4つの状態と動作があります。
それは4つの州があります:25セント、25セントなし、キャンディー販売、キャンディー完売。
さらに5つのアクションがあります。25ポイントを投資し、25ポイントを返し、クランクを回し、キャンディーを配り、詰め替えます
次に、状態と動作に基づいて、4つの状態、現在の状態、現在のキャンディの数、および5つの動作に対応するキャンディマシンクラスを作成します。
キャンディマシンを操作すると、キャンディマシンの現在の状態に応じて、対応する結果のフィードバックが最初に得られます。
たとえば、キャンディマシンは25ポイントの状態ではありませんが、クランクを回す必要があります。状態は変化せず、対応するプロンプトが表示されるため、ステートマシンの各状態は対応する操作とその他の操作のみを持つことができます。無理であり、対応するプロンプトが表示されます
package headfirst.designpatterns.state.gumball;
public class GumballMachine {
final static int SOLD_OUT = 0; //售完
final static int NO_QUARTER = 1; //没有25分
final static int HAS_QUARTER = 2; //有25分
final static int SOLD = 3; //售出
int state = SOLD_OUT; //状态变量,表示当前的状态
int count = 0; //当前糖果的数目
/**
* 状态机初始化,传入糖果数目,状态变为没有25分
* @param count
*/
public GumballMachine(int count) {
this.count = count;
if (count > 0) {
state = NO_QUARTER;
}
}
/**
* 投入25分
*/
public void insertQuarter() {
if (state == HAS_QUARTER) {
System.out.println("You can't insert another quarter");
} else if (state == NO_QUARTER) {
state = HAS_QUARTER;
System.out.println("You inserted a quarter");
} else if (state == SOLD_OUT) {
System.out.println("You can't insert a quarter, the machine is sold out");
} else if (state == SOLD) {
System.out.println("Please wait, we're already giving you a gumball");
}
}
/**
* 退回25分
*/
public void ejectQuarter() {
if (state == HAS_QUARTER) {
System.out.println("Quarter returned");
state = NO_QUARTER;
} else if (state == NO_QUARTER) {
System.out.println("You haven't inserted a quarter");
} else if (state == SOLD) {
System.out.println("Sorry, you already turned the crank");
} else if (state == SOLD_OUT) {
System.out.println("You can't eject, you haven't inserted a quarter yet");
}
}
/**
* 转动曲柄
*/
public void turnCrank() {
if (state == SOLD) {
System.out.println("Turning twice doesn't get you another gumball!");
} else if (state == NO_QUARTER) {
System.out.println("You turned but there's no quarter");
} else if (state == SOLD_OUT) {
System.out.println("You turned, but there are no gumballs");
} else if (state == HAS_QUARTER) {
System.out.println("You turned...");
state = SOLD;
dispense();
}
}
/**
* 发放糖果
*/
private void dispense() {
if (state == SOLD) {
System.out.println("A gumball comes rolling out the slot");
count = count - 1;
if (count == 0) {
System.out.println("Oops, out of gumballs!");
state = SOLD_OUT;
} else {
state = NO_QUARTER;
}
} else if (state == NO_QUARTER) {
System.out.println("You need to pay first");
} else if (state == SOLD_OUT) {
System.out.println("No gumball dispensed");
} else if (state == HAS_QUARTER) {
System.out.println("No gumball dispensed");
}
}
/**
* 装入糖果
*/
public void refill(int numGumBalls) {
this.count = numGumBalls;
state = NO_QUARTER;
}
public String toString() {
StringBuffer result = new StringBuffer();
result.append("\nMighty Gumball, Inc.");
result.append("\nJava-enabled Standing Gumball Model #2004\n");
result.append("Inventory: " + count + " gumball");
if (count != 1) {
result.append("s");
}
result.append("\nMachine is ");
if (state == SOLD_OUT) {
result.append("sold out");
} else if (state == NO_QUARTER) {
result.append("waiting for quarter");
} else if (state == HAS_QUARTER) {
result.append("waiting for turn of crank");
} else if (state == SOLD) {
result.append("delivering a gumball");
}
result.append("\n");
return result.toString();
}
}
以下はテストクラスです:キャンディマシンを使用してプレイヤーをシミュレートする
package headfirst.designpatterns.state.gumball;
public class GumballMachineTestDrive {
public static void main(String[] args) {
//初始化糖果机为5个
GumballMachine gumballMachine = new GumballMachine(5);
System.out.println(gumballMachine);
//投入25分
gumballMachine.insertQuarter();
//转动曲柄
gumballMachine.turnCrank();
System.out.println(gumballMachine);
//投入25分
gumballMachine.insertQuarter();
//退回25分
gumballMachine.ejectQuarter();
//转动曲柄
gumballMachine.turnCrank();
System.out.println(gumballMachine);
gumballMachine.insertQuarter();
gumballMachine.insertQuarter();
gumballMachine.turnCrank();
gumballMachine.insertQuarter();
gumballMachine.turnCrank();
gumballMachine.insertQuarter();
gumballMachine.turnCrank();
System.out.println(gumballMachine);
}
}
キャンディマシンクラスを使用して、対応する機能を完成させます
この時点で、キャンディー会社には新しい需要があります。顧客がクランクを回すと、10%の確率で勝者になります。勝者状態のプレイヤーは、以下に示すように2つのキャンディーを獲得できます。
上記の要件に対応して、前のコードを変更し、最初に勝者の状態を追加し、次に各メソッドに対応するロジックを追加する必要がありますが、これは開閉の原則を満たしていません。
次に、コードを書き直します。
1.まず、状態インターフェースを設計する必要があります。このインターフェースでは、キャンディーマシンの各アクションに対応するメソッドがあります。
2.次に、マシンの各状態のオブジェクトを設計し、状態インターフェイスを実装します
3.マシンを操作すると、ステートオブジェクトに直接渡されて処理されます。
ファーストクラス図
最初に状態インターフェースを見てみましょう
package state.demo1;
/**
* 状态接口
*/
public interface State {
/**
* 加入25分
*/
public void insertQuarter();
/**
* 退回25分
*/
public void ejectQuarter();
/**
* 转动曲柄
*/
public void turnCrank();
/**
* 发放糖果
*/
public void dispense();
/**
* 装入糖果
*/
public void refill();
}
次に、5つの状態クラスを作成します
1. 25ポイントのない状態クラス
package state.demo1;
/**
* 没有25分状态
*/
public class NoQuarterState implements State {
GumballMachine gumballMachine;
public NoQuarterState(GumballMachine gumballMachine) {
this.gumballMachine = gumballMachine;
}
public void insertQuarter() {
System.out.println("You inserted a quarter");
gumballMachine.setState(gumballMachine.getHasQuarterState());
}
public void ejectQuarter() {
System.out.println("You haven't inserted a quarter");
}
public void turnCrank() {
System.out.println("You turned, but there's no quarter");
}
public void dispense() {
System.out.println("You need to pay first");
}
public void refill() { }
public String toString() {
return "waiting for quarter";
}
}
2. 25ポイントのステータスクラス
package state.demo1;
import java.util.Random;
/**
* 有25分状态
*/
public class HasQuarterState implements State {
Random randomWinner = new Random(System.currentTimeMillis());
GumballMachine gumballMachine;
public HasQuarterState(GumballMachine gumballMachine) {
this.gumballMachine = gumballMachine;
}
public void insertQuarter() {
System.out.println("You can't insert another quarter");
}
public void ejectQuarter() {
System.out.println("Quarter returned");
gumballMachine.setState(gumballMachine.getNoQuarterState());
}
public void turnCrank() {
System.out.println("You turned...");
int winner = randomWinner.nextInt(10);
if ((winner == 0) && (gumballMachine.getCount() > 1)) {
gumballMachine.setState(gumballMachine.getWinnerState());
} else {
gumballMachine.setState(gumballMachine.getSoldState());
}
}
public void dispense() {
System.out.println("No gumball dispensed");
}
public void refill() { }
public String toString() {
return "waiting for turn of crank";
}
}
3.お菓子の販売状況
package state.demo1;
/**
* 出售状态
*/
public class SoldState implements State {
GumballMachine gumballMachine;
public SoldState(GumballMachine gumballMachine) {
this.gumballMachine = gumballMachine;
}
public void insertQuarter() {
System.out.println("Please wait, we're already giving you a gumball");
}
public void ejectQuarter() {
System.out.println("Sorry, you already turned the crank");
}
public void turnCrank() {
System.out.println("Turning twice doesn't get you another gumball!");
}
public void dispense() {
gumballMachine.releaseBall();
if (gumballMachine.getCount() > 0) {
gumballMachine.setState(gumballMachine.getNoQuarterState());
} else {
System.out.println("Oops, out of gumballs!");
gumballMachine.setState(gumballMachine.getSoldOutState());
}
}
public void refill() { }
public String toString() {
return "dispensing a gumball";
}
}
4.売り切れステータスクラス
package state.demo1;
/**
* 售完状态
*/
public class SoldOutState implements State {
GumballMachine gumballMachine;
public SoldOutState(GumballMachine gumballMachine) {
this.gumballMachine = gumballMachine;
}
public void insertQuarter() {
System.out.println("You can't insert a quarter, the machine is sold out");
}
public void ejectQuarter() {
System.out.println("You can't eject, you haven't inserted a quarter yet");
}
public void turnCrank() {
System.out.println("You turned, but there are no gumballs");
}
public void dispense() {
System.out.println("No gumball dispensed");
}
public void refill() {
gumballMachine.setState(gumballMachine.getNoQuarterState());
}
public String toString() {
return "sold out";
}
}
5.勝者のステータス
package state.demo1;
/**
* 赢家状态
*/
public class WinnerState implements State {
GumballMachine gumballMachine;
public WinnerState(GumballMachine gumballMachine) {
this.gumballMachine = gumballMachine;
}
public void insertQuarter() {
System.out.println("Please wait, we're already giving you a Gumball");
}
public void ejectQuarter() {
System.out.println("Please wait, we're already giving you a Gumball");
}
public void turnCrank() {
System.out.println("Turning again doesn't get you another gumball!");
}
public void dispense() {
gumballMachine.releaseBall();
if (gumballMachine.getCount() == 0) {
gumballMachine.setState(gumballMachine.getSoldOutState());
} else {
gumballMachine.releaseBall();
System.out.println("YOU'RE A WINNER! You got two gumballs for your quarter");
if (gumballMachine.getCount() > 0) {
gumballMachine.setState(gumballMachine.getNoQuarterState());
} else {
System.out.println("Oops, out of gumballs!");
gumballMachine.setState(gumballMachine.getSoldOutState());
}
}
}
public void refill() { }
public String toString() {
return "despensing two gumballs for your quarter, because YOU'RE A WINNER!";
}
}
以下はキャンディーマシンのクラスです
package state.demo1;
/**
* 糖果机
*/
public class GumballMachine {
/**
* 糖果机的所有状态
*/
State soldOutState;
State noQuarterState;
State hasQuarterState;
State soldState;
State winnerState;
State state = soldOutState; //糖果机在开始是没有糖果的
int count = 0;
/**
* 初始化糖果机数量,为每一种状态都创建一个对象,并将糖果机加入到每个状态对象中
* @param numberGumballs
*/
public GumballMachine(int numberGumballs) {
soldOutState = new SoldOutState(this);
noQuarterState = new NoQuarterState(this);
hasQuarterState = new HasQuarterState(this);
soldState = new SoldState(this);
winnerState = new WinnerState(this);
this.count = numberGumballs;
if (numberGumballs > 0) {
state = noQuarterState;
}
}
/**
* 投入25分
*/
public void insertQuarter() {
state.insertQuarter();
}
/**
* 退回25分
*/
public void ejectQuarter() {
state.ejectQuarter();
}
/**
* 转动曲柄,我们不需要在糖果机准备一个dispense()动作方法,因为这是一个内部动作
* 用户不可以直接要求机器发放糖果,转动曲柄后如果是赢家或者售出状态,可以直接获得球
*/
public void turnCrank() {
state.turnCrank();
state.dispense();
}
/**
* 装入糖果
* @param count
*/
public void refill(int count) {
this.count += count;
System.out.println("The gumball machine was just refilled; it's new count is: " + this.count);
state.refill();
}
/**
*释放一个糖果
*/
public void releaseBall() {
System.out.println("A gumball comes rolling out the slot...");
if (count != 0) {
count = count - 1;
}
}
/**
* 设置状态
* @param state
*/
public void setState(State state) {
this.state = state;
}
public int getCount() {
return count;
}
public State getState() {
return state;
}
public State getSoldOutState() {
return soldOutState;
}
public State getNoQuarterState() {
return noQuarterState;
}
public State getHasQuarterState() {
return hasQuarterState;
}
public State getSoldState() {
return soldState;
}
public State getWinnerState() {
return winnerState;
}
public String toString() {
StringBuffer result = new StringBuffer();
result.append("\nMighty Gumball, Inc.");
result.append("\nJava-enabled Standing Gumball Model #2004");
result.append("\nInventory: " + count + " gumball");
if (count != 1) {
result.append("s");
}
result.append("\n");
result.append("Machine is " + state + "\n");
return result.toString();
}
}
テストクラス
package state.demo1;
public class Client {
public static void main(String[] args) {
GumballMachine gumballMachine =
new GumballMachine(10);
System.out.println(gumballMachine);
//第一次开始玩游戏
gumballMachine.insertQuarter();
gumballMachine.turnCrank();
gumballMachine.insertQuarter();
gumballMachine.turnCrank();
System.out.println(gumballMachine);
//第二次玩游戏
gumballMachine.turnCrank();
gumballMachine.refill(10);
gumballMachine.insertQuarter();
gumballMachine.turnCrank();
}
}
運用実績
要約:
比較する前に、主に次のコンテンツを最適化しました
1.各状態の動作を独自のクラスにローカライズする
2.問題が発生しやすいifステートメントを削除します。将来のメンテナンスのため
3.新しい状態クラスを追加できるため、キャンディマシン内のすべての状態を変更のために閉じ、キャンディマシンを外側に拡張させます。
4.キャンディマシンは、処理を現在の状態オブジェクトに委任するだけで、内部コードを簡素化できます。