狀態模式(State pattern): 允許對象在內部狀態改變時改變它的行爲, 對象看起來好象改了它的類。
狀態模式的詳解
簡單描述,狀態模式把所研究對象的行文包裝在不同的狀態對象裏,所有的狀態對象都是抽象狀態的子類。狀態模式的目的就是一個狀態對象在其內部改變的時候,此狀態對象的行爲也隨之改變。
類圖:
角色說明:
抽象狀態角色(State):定義一個接口,用以封裝環境(Context)對象的一個特定的狀態所對應的行爲。
具體狀態角色(ConcreteState):每一個具體狀態都實現了環境(Context)的一個狀態所對應的行爲。
環境角色(Context):也是上下文意思,定義客戶端所感興趣的接口,並且保留一個具體狀態的實例。這個具體狀態類的實例給出此環境對象的現有狀態。
代碼演示,抽象狀態類:
public interface State {
/**
* 狀態對應的改變
*/
public void change(String sampleParameter);
}
環境狀態類:
public class Context {
// 持有一個State類型的對象實例
private State state;
public void setState(State state) {
this.state = state;
}
/**
* 用戶感興趣的接口方法
*/
public void request(String sampleParameter) {
// 轉調state來處理
state.change(sampleParameter);
}
}
具體狀態類1:
public class ConcreteState1 implements State {
@Override
public void change(String sampleParameter) {
System.out.println("具體狀態類1進行行爲改變:" + sampleParameter);
}
}
具體狀態類2:
public class ConcreteState2 implements State {
@Override
public void change(String sampleParameter) {
System.out.println("具體狀態類2進行行爲改變:" + sampleParameter);
}
}
客戶端測試類:
public class Client {
public static void main(String[] args) {
// 創建狀態
State state = new ConcreteState1();
// 創建環境
Context context = new Context();
// 將狀態設置到環境中
context.setState(state);
// 請求
context.request("Holle world");
}
}
運行結果:
從上面的例子可以看出,環境狀態類裏面是通過行爲request()來委派一個具體狀態類來改變的。通過多態性原則,我們可以隨意改變環境狀態類裏面的state屬性,使其可以變換的指向任意一個具體狀態角色,從而使環境狀態類的行爲request()裏面由不同的具體狀態類來執行。
適用場景
一個在線投票系統,要實現控制同一個用戶只能投一次票。如果同一個用戶投一次票是投票成功;再投就記爲重複投票;超過5次就記爲惡意刷票,將取消此用戶的投票記錄;超過8次就記爲黑名單,禁止登陸和使用本系統。
用狀態模式來實現,抽象狀態角色(State)就是投票的狀態,而具體狀態角色(ConcreteState)就有:正常投票、反覆投票、惡意刷票、進入黑名單。再創建一個投票管理的對象,這個就相當於環境狀態角色(Context)。
類圖:
代碼演示,抽象的投票狀態類:
public interface VoteState {
/**
* 狀態對應行爲的處理
*
* @param user
* 投票人
* @param voteItem
* 投票項
* @param voteManager
* 投票上下文,當需要改變投票狀態時可以回調上下文數據
*/
public void vote(String user, String voteItem, VoteManager voteManager);
}
正常投票類:
public class NormalVoteState implements VoteState {
@Override
public void vote(String user, String voteItem, VoteManager voteManager) {
// 正常投票,記錄到投票記錄中
voteManager.getMapVote().put(user, voteItem);
System.out.println(user+",恭喜你投票成功");
}
}
重複投票類:
public class RepeatVoteState implements VoteState {
@Override
public void vote(String user, String voteItem, VoteManager voteManager) {
// 重複投票,暫時不做處理
System.out.println(user + ",請不要重複投票");
}
}
惡意投票類:
public class SpiteVoteState implements VoteState {
@Override
public void vote(String user, String voteItem, VoteManager voteManager) {
// 惡意投票,取消用戶的投票資格,並取消投票記錄
String str = voteManager.getMapVote().get(user);
if (str != null) {
voteManager.getMapVote().remove(user);
}
System.out.println(user + ",你有惡意刷屏行爲,取消投票資格");
}
}
進入黑名單類:
public class BlackVoteState implements VoteState {
@Override
public void vote(String user, String voteItem, VoteManager voteManager) {
// 記錄進入黑名單,禁止登錄系統
System.out.println(user + ",你已進入黑名單,將禁止登錄和使用本系統");
}
}
投票管理類:
public class VoteManager {
// 持有具體狀態對象
private VoteState state = null;
// 記錄用戶投票的結果,數據記錄爲Map<用戶名稱,投票的選項>
private Map<String, String> mapVote = new HashMap<String, String>();
// 記錄用戶投票次數,數據記錄爲Map<用戶名稱,投票的次數>
private Map<String, Integer> mapVoteCount = new HashMap<String, Integer>();
/**
* 獲取用戶投票結果的Map
*/
public Map<String, String> getMapVote() {
return mapVote;
}
/**
* 投票邏輯處理方法
*
* @param user 投票人
*
* @param voteItem 投票的選項
*/
public void vote(String user, String voteItem) {
// 1.爲該用戶增加投票次數
// 從記錄中取出該用戶已有的投票次數
Integer oldVoteCount = mapVoteCount.get(user);
if (oldVoteCount == null) {
// 該用戶第一次投票情況,先實例化爲0,而後再添加
oldVoteCount = 0;
}
oldVoteCount += 1;
mapVoteCount.put(user, oldVoteCount);
// 2.判斷該用戶的投票類型,就相當於判斷對應的狀態
// 到底是正常投票、重複投票、惡意投票還是上黑名單的狀態
if (oldVoteCount == 1) {
state = new NormalVoteState();
} else if (oldVoteCount > 1 && oldVoteCount < 5) {
state = new RepeatVoteState();
} else if (oldVoteCount >= 5 && oldVoteCount < 8) {
state = new SpiteVoteState();
} else if (oldVoteCount > 8) {
state = new BlackVoteState();
}
// 然後轉調狀態對象來進行相應的操作
state.vote(user, voteItem, this);
}
}
客戶端測試類:
public class Client {
public static void main(String[] args) {
VoteManager vm = new VoteManager();
for (int i = 0; i < 11; i++) {
// 張三,投票B選項
vm.vote("張三", "B");
}
}
}
運行結果:
狀態模式的總結
理解狀態模式的狀態和行爲,狀態就是指對象中屬性的值,而行爲是指對象中的功能、或說對象中的方法。
狀態模式的目的就是分離狀態的行爲,通過維護狀態的變化來調用不同狀態對應的不同功能。即是說,狀態和行爲是相關聯的,關係大概是:狀態決定行爲。
狀態模式中的狀態是運行時期才被改變的,所以行爲也會在運行期隨着狀態的改變而改變。
狀態模式的優點:
- 狀態模式將具體的狀態和與之相關的行爲進行局部化,而後將不同的具體狀態分割開來,易於管理和維護。
- 正是將不同的具體狀態分割開來,以減少不同狀態之間的耦合。
- 所有特定的狀態和行爲都分別封裝在一個具體的狀態中,這種情況下,很容易增加新的狀態、以及實現狀態的裝換。
缺點:
- 當業務邏輯慢慢的擴大之後,會導致過多的具體狀態類。並且環境狀態角色會變得臃腫,變得難以管理和維護。