變身藥丸
黑崎一護在和藍染戰鬥中,使用了最後的月牙天衝重創藍染,最終被被浦原喜助使用封印封鎖。
一護立下大功,同時也喪失了死神的力量,無法使用斬魂刀變身了。
十二番隊長涅繭利爲了感謝一戶,苦心研發了能夠暫時獲得死神能力的三種藥丸:初解藥丸,卍解藥丸和虛化藥丸。
這三種藥丸服用之後能夠變身成相應的狀態,同時擁有對應的必殺技,戰鬥力大大增強。
黑崎一護的變身狀態一覽
正常狀態
戰鬥力指數:100
始解狀態
戰鬥力指數:200
卍解狀態
戰鬥力指數:500
虛化狀態
戰鬥力指數:1000
十二番隊長涅繭利特意囑咐了下用藥規則,正常狀態下只有喫始解藥丸和卍解藥丸纔有作用,虛化藥丸只有在卍解狀態下喫纔有作用,還有,同一種藥丸重複喫是沒有用的,不要浪費哦。
另外,變身狀態下,使用了必殺技之後,藥效就會消失,立即恢復到正常狀態,要注意哦,副隊長涅音夢提醒道。
貼心的副隊長涅音夢還畫了幅狀態轉換圖:
普通的實現方法
狀態枚舉類
public enum State {
/**
* 正常狀態
*/
NORMAL(0),
/**
* 始解狀態
*/
INITIAL(1),
/**
* 卍解狀態
*/
SWASTIKA(2),
/**
* 虛化狀態
*/
BLUR(3);
private int value;
private State(int value) {
this.value = value;
}
public int getValue() {
return this.value;
}
}
變身狀態機類
public class TransformStateMachine {
/**
* 戰鬥力
*/
private int fightingPower;
/**
* 當前狀態
*/
private State currentState;
public int getFightingPower() {
return this.fightingPower;
}
public State getCurrentState() {
return this.currentState;
}
/**
* 初始化
*/
public TransformStateMachine() {
this.fightingPower = 100;
this.currentState = State.NORMAL;
}
/**
* 喫下始解藥丸
*/
public void eateInitialPill() {
System.out.println("喫下始解藥丸");
if(currentState.equals(State.NORMAL)) {
this.currentState = State.INITIAL;
this.fightingPower += 200;
System.out.println("**變身始解狀態**戰鬥力加200");
} else {
System.out.println("木有任何變化。。。");
}
}
/**
* 喫下卍解藥丸
*/
public void eateSwastikaPill() {
System.out.println("喫下卍解藥丸");
if(currentState.equals(State.NORMAL) || currentState.equals(State.INITIAL)) {
this.currentState = State.SWASTIKA;
this.fightingPower += 500;
System.out.println("**變身卍解狀態**戰鬥力加500");
} else {
System.out.println("木有任何變化。。。");
}
}
/**
* 喫下虛化藥丸
*/
public void eateBlurPill() {
System.out.println("喫下虛化藥丸");
if(currentState.equals(State.SWASTIKA)) {
this.currentState = State.BLUR;
this.fightingPower += 1000;
System.out.println("**變身虛化狀態**戰鬥力加1000");
} else {
System.out.println("木有任何變化。。。");
}
}
/**
* 使用必殺技
*/
public void useUniqueSkill() {
System.out.println("使用必殺技");
if(currentState.equals(State.INITIAL) || currentState.equals(State.SWASTIKA) || currentState.equals(State.BLUR)) {
System.out.println("月牙天衝--->");
this.currentState = State.NORMAL;
this.fightingPower = 100;
System.out.println("**變回初始狀態**戰鬥力變成100");
} else {
System.out.println("木有任何變化。。。");
}
}
}
測試類
public class Test {
public static void main(String[] args) {
TransformStateMachine transformStateMachine = new TransformStateMachine();
System.out.println("當前狀態: " + transformStateMachine.getCurrentState() + "; 戰鬥力指數: " + transformStateMachine.getFightingPower() + '\n');
transformStateMachine.eateInitialPill();
System.out.println("當前狀態: " + transformStateMachine.getCurrentState() + "; 戰鬥力指數: " + transformStateMachine.getFightingPower() + '\n');
transformStateMachine.eateSwastikaPill();
System.out.println("當前狀態: " + transformStateMachine.getCurrentState() + "; 戰鬥力指數: " + transformStateMachine.getFightingPower() + '\n');
transformStateMachine.eateBlurPill();
System.out.println("當前狀態: " + transformStateMachine.getCurrentState() + "; 戰鬥力指數: " + transformStateMachine.getFightingPower() + '\n');
transformStateMachine.useUniqueSkill();
System.out.println("當前狀態: " + transformStateMachine.getCurrentState() + "; 戰鬥力指數: " + transformStateMachine.getFightingPower() + '\n');
transformStateMachine.useUniqueSkill();
System.out.println("當前狀態: " + transformStateMachine.getCurrentState() + "; 戰鬥力指數: " + transformStateMachine.getFightingPower() + '\n');
}
}
運行結果
當前狀態: NORMAL; 戰鬥力指數: 100
喫下始解藥丸
**變身始解狀態**戰鬥力加200
當前狀態: INITIAL; 戰鬥力指數: 300
喫下卍解藥丸
**變身卍解狀態**戰鬥力加500
當前狀態: SWASTIKA; 戰鬥力指數: 800
喫下虛化藥丸
**變身虛化狀態**戰鬥力加1000
當前狀態: BLUR; 戰鬥力指數: 1800
使用必殺技
月牙天衝--->
**變回初始狀態**戰鬥力變成100
當前狀態: NORMAL; 戰鬥力指數: 100
使用必殺技
木有任何變化。。。
當前狀態: NORMAL; 戰鬥力指數: 100
如果是簡單的狀態轉換來說,這種方式是最簡單直接的首選方法。
如果狀態很多,這樣編寫的代碼會包含大量的 if-else 或 switch-case 分支判斷邏輯,可讀性和可維護性都很差。
而且不符合開閉原則,如果修改了狀態機中的某個狀態轉移,我們要在冗長的分支邏輯中找到對應的代碼進行修改,很容易改錯,引入 bug。
使用狀態模式
我們要重構一下上面的代碼,試着把每個狀態的行爲封裝到一個類裏,“封裝了變化”,這樣能讓邏輯和結構更加清晰。
而且,我們針對某個狀態的改變就不會影響其他的狀態了,讓修改和維護更容易。
這時候就要請狀態模式出馬了。
狀態模式(State Pattern) :允許一個對象在其內部狀態改變時改變它的行爲,對象看起來似乎修改了它的類。
本例的類圖如下:
狀態枚舉類
public enum State {
/**
* 正常狀態
*/
NORMAL(0),
/**
* 始解狀態
*/
INITIAL(1),
/**
* 卍解狀態
*/
SWASTIKA(2),
/**
* 虛化狀態
*/
BLUR(3);
private int value;
private State(int value) {
this.value = value;
}
public int getValue() {
return this.value;
}
}
狀態類接口
public interface ITransformState {
/**
* 獲取當前狀態
* @return
*/
State getState();
/**
* 喫下始解藥丸
*/
void eateInitialPill();
/**
* 喫下卍解藥丸
*/
void eateSwastikaPill();
/**
* 喫下虛化藥丸
*/
void eateBlurPill();
/**
* 使用必殺技
*/
void useUniqueSkill();
}
初始狀態實現類
public class NormalTransform implements ITransformState{
private TransformStateMachine transformStateMachine;
public NormalTransform(TransformStateMachine transformStateMachine) {
this.transformStateMachine = transformStateMachine;
}
/**
* 獲取當前狀態
* @return
*/
public State getState() {
return State.NORMAL;
}
/**
* 喫下始解藥丸
*/
public void eateInitialPill() {
System.out.println("喫下始解藥丸");
transformStateMachine.setCurrentState(new InitialTransform(transformStateMachine));
transformStateMachine.setFightingPower(transformStateMachine.getFightingPower() + 200);
System.out.println("**變身始解狀態**戰鬥力加200");
}
/**
* 喫下卍解藥丸
*/
public void eateSwastikaPill() {
System.out.println("喫下卍解藥丸");
transformStateMachine.setCurrentState(new SwastikaTransform(transformStateMachine));
transformStateMachine.setFightingPower(transformStateMachine.getFightingPower() + 500);
System.out.println("**變身卍解狀態**戰鬥力加500");
}
/**
* 喫下虛化藥丸
*/
public void eateBlurPill() {
System.out.println("喫下虛化藥丸");
System.out.println("木有任何變化。。。");
}
/**
* 使用必殺技
*/
public void useUniqueSkill() {
System.out.println("使用必殺技");
System.out.println("木有任何變化。。。");
}
}
始解狀態實現類
public class InitialTransform implements ITransformState{
private TransformStateMachine transformStateMachine;
public InitialTransform(TransformStateMachine transformStateMachine) {
this.transformStateMachine = transformStateMachine;
}
/**
* 獲取當前狀態
* @return
*/
public State getState() {
return State.INITIAL;
}
/**
* 喫下始解藥丸
*/
public void eateInitialPill() {
System.out.println("喫下始解藥丸");
System.out.println("木有任何變化。。。");
}
/**
* 喫下卍解藥丸
*/
public void eateSwastikaPill() {
System.out.println("喫下卍解藥丸");
transformStateMachine.setCurrentState(new SwastikaTransform(transformStateMachine));
transformStateMachine.setFightingPower(transformStateMachine.getFightingPower() + 500);
System.out.println("**變身卍解狀態**戰鬥力加500");
}
/**
* 喫下虛化藥丸
*/
public void eateBlurPill() {
System.out.println("喫下虛化藥丸");
System.out.println("木有任何變化。。。");
}
/**
* 使用必殺技
*/
public void useUniqueSkill() {
System.out.println("使用必殺技");
System.out.println("月牙天衝--->");
transformStateMachine.setCurrentState(new NormalTransform(transformStateMachine));
transformStateMachine.setFightingPower(100);
System.out.println("**變回初始狀態**戰鬥力變成100");
}
}
卍解狀態實現類
public class SwastikaTransform implements ITransformState {
private TransformStateMachine transformStateMachine;
public SwastikaTransform(TransformStateMachine transformStateMachine) {
this.transformStateMachine = transformStateMachine;
}
/**
* 獲取當前狀態
* @return
*/
public State getState() {
return State.SWASTIKA;
}
/**
* 喫下始解藥丸
*/
public void eateInitialPill() {
System.out.println("喫下始解藥丸");
System.out.println("木有任何變化。。。");
}
/**
* 喫下卍解藥丸
*/
public void eateSwastikaPill() {
System.out.println("喫下始解藥丸");
System.out.println("木有任何變化。。。");
}
/**
* 喫下虛化藥丸
*/
public void eateBlurPill() {
System.out.println("喫下虛化藥丸");
transformStateMachine.setCurrentState(new BlurTransform(transformStateMachine));
transformStateMachine.setFightingPower(transformStateMachine.getFightingPower() + 1000);
System.out.println("**變身虛化狀態**戰鬥力加1000");
}
/**
* 使用必殺技
*/
public void useUniqueSkill() {
System.out.println("使用必殺技");
System.out.println("黑流牙突--->");
transformStateMachine.setCurrentState(new NormalTransform(transformStateMachine));
transformStateMachine.setFightingPower(100);
System.out.println("**變回初始狀態**戰鬥力變成100");
}
}
虛化狀態實現類
public class BlurTransform implements ITransformState {
private TransformStateMachine transformStateMachine;
public BlurTransform(TransformStateMachine transformStateMachine) {
this.transformStateMachine = transformStateMachine;
}
/**
* 獲取當前狀態
* @return
*/
public State getState() {
return State.BLUR;
}
/**
* 喫下始解藥丸
*/
public void eateInitialPill() {
System.out.println("喫下始解藥丸");
System.out.println("木有任何變化。。。");
}
/**
* 喫下卍解藥丸
*/
public void eateSwastikaPill() {
System.out.println("喫下卍解藥丸");
System.out.println("木有任何變化。。。");
}
/**
* 喫下虛化藥丸
*/
public void eateBlurPill() {
System.out.println("喫下虛化藥丸");
System.out.println("木有任何變化。。。");
}
/**
* 使用必殺技
*/
public void useUniqueSkill() {
System.out.println("使用必殺技");
System.out.println("王虛的閃光--->");
transformStateMachine.setCurrentState(new NormalTransform(transformStateMachine));
transformStateMachine.setFightingPower(100);
System.out.println("**變回初始狀態**戰鬥力變成100");
}
}
變身狀態機類
public class TransformStateMachine {
/**
* 戰鬥力
*/
private int fightingPower;
/**
* 當前狀態
*/
private ITransformState currentState;
/**
* 初始化
*/
public TransformStateMachine() {
this.fightingPower = 100;
this.currentState = new NormalTransform(this);
}
/**
* 喫下始解藥丸
*/
public void eateInitialPill() {
this.currentState.eateInitialPill();
}
/**
* 喫下卍解藥丸
*/
public void eateSwastikaPill() {
this.currentState.eateSwastikaPill();
}
/**
* 喫下虛化藥丸
*/
public void eateBlurPill() {
this.currentState.eateBlurPill();
}
/**
* 使用必殺技
*/
public void useUniqueSkill() {
this.currentState.useUniqueSkill();
}
public int getFightingPower() {
return this.fightingPower;
}
public void setFightingPower(int fightingPower) {
this.fightingPower = fightingPower;
}
public State getCurrentState() {
return this.currentState.getState();
}
public void setCurrentState(ITransformState currentState) {
this.currentState = currentState;
}
}
測試類
public class Test {
public static void main(String[] args) {
TransformStateMachine transformStateMachine = new TransformStateMachine();
System.out.println("當前狀態: " + transformStateMachine.getCurrentState() + "; 戰鬥力指數: " + transformStateMachine.getFightingPower() + '\n');
transformStateMachine.eateInitialPill();
System.out.println("當前狀態: " + transformStateMachine.getCurrentState() + "; 戰鬥力指數: " + transformStateMachine.getFightingPower() + '\n');
transformStateMachine.eateBlurPill();
System.out.println("當前狀態: " + transformStateMachine.getCurrentState() + "; 戰鬥力指數: " + transformStateMachine.getFightingPower() + '\n');
transformStateMachine.eateSwastikaPill();
System.out.println("當前狀態: " + transformStateMachine.getCurrentState() + "; 戰鬥力指數: " + transformStateMachine.getFightingPower() + '\n');
transformStateMachine.eateBlurPill();
System.out.println("當前狀態: " + transformStateMachine.getCurrentState() + "; 戰鬥力指數: " + transformStateMachine.getFightingPower() + '\n');
transformStateMachine.useUniqueSkill();
System.out.println("當前狀態: " + transformStateMachine.getCurrentState() + "; 戰鬥力指數: " + transformStateMachine.getFightingPower() + '\n');
transformStateMachine.eateSwastikaPill();
System.out.println("當前狀態: " + transformStateMachine.getCurrentState() + "; 戰鬥力指數: " + transformStateMachine.getFightingPower() + '\n');
transformStateMachine.useUniqueSkill();
System.out.println("當前狀態: " + transformStateMachine.getCurrentState() + "; 戰鬥力指數: " + transformStateMachine.getFightingPower() + '\n');
}
}
測試結果
當前狀態: NORMAL; 戰鬥力指數: 100
喫下始解藥丸
**變身始解狀態**戰鬥力加200
當前狀態: INITIAL; 戰鬥力指數: 300
喫下虛化藥丸
木有任何變化。。。
當前狀態: INITIAL; 戰鬥力指數: 300
喫下卍解藥丸
**變身卍解狀態**戰鬥力加500
當前狀態: SWASTIKA; 戰鬥力指數: 800
喫下虛化藥丸
**變身虛化狀態**戰鬥力加1000
當前狀態: BLUR; 戰鬥力指數: 1800
使用必殺技
王虛的閃光--->
**變回初始狀態**戰鬥力變成100
當前狀態: NORMAL; 戰鬥力指數: 100
喫下卍解藥丸
**變身卍解狀態**戰鬥力加500
當前狀態: SWASTIKA; 戰鬥力指數: 600
使用必殺技
黑流牙突--->
**變回初始狀態**戰鬥力變成100
當前狀態: NORMAL; 戰鬥力指數: 100
總結
狀態模式一般用來實現有限狀態機。
有限狀態機的英文翻譯是 Finite State Machine,縮寫爲 FSM,簡稱爲狀態機。
狀態機有 3 個組成部分:狀態(State)、事件(Event)、動作(Action)。其中,事件也稱爲轉移條件(Transition Condition)。事件觸發狀態的轉移及動作的執行。
不過,動作不是必須的,也可能只轉移狀態,不執行任何動作。
狀態機的狀態隨着某種事件的發生而改變,事件就是狀態轉移的條件,狀態轉移時可能會有相應的操作要同時進行。
狀態模式的優點
1.如果狀態變更伴隨着複雜的操作,狀態模式能把所有操作封裝到一個類裏,允許狀態轉換邏輯與狀態對象合成一體,而不是某一個巨大的條件語句塊。
2.把每個狀態和動作提取出來,封裝到一個類中,就把着眼點從執行狀態提高到整個對象的狀態,這將使代碼更加結構化,整體意圖更加清晰。
3.以後對狀態的修改之需要修改一個子類,新增一個新的狀態,只需要新增一個狀態類,對原有代碼的修改少。
狀態模式的缺點
1.會增加系統類和對象的個數。
2.一個State的子類至少擁有一個其他子類的信息,各個子類之間產生了依賴。