原文地址 折騰Java設計模式之狀態模式html
在狀態模式(State Pattern)中,類的行爲是基於它的狀態改變的。這種類型的設計模式屬於行爲型模式。在狀態模式中,咱們建立表示各類狀態的對象和一個行爲隨着狀態對象改變而改變的 context 對象。通俗點就是一個對象在內部狀態發生改變時改變它的行爲。java
意圖 容許對象在內部狀態發生改變時改變它的行爲,對象看起來好像修改了它的類。git
主要解決 對象的行爲依賴於它的狀態(屬性),而且能夠根據它的狀態改變而改變它的相關行爲。github
什麼時候使用 代碼中包含大量與對象狀態有關的條件語句。算法
如何解決 將各類具體的狀態類抽象出來。設計模式
關鍵代碼 一般命令模式的接口中只有一個方法。而狀態模式的接口中有一個或者多個方法。並且,狀態模式的實現類的方法,通常返回值,或者是改變實例變量的值。也就是說,狀態模式通常和對象的狀態有關。實現類的方法有不一樣的功能,覆蓋接口中的方法。狀態模式和命令模式同樣,也能夠用於消除 if...else 等條件選擇語句。微信
UML圖ide
主要角色this
1)Context(環境類):環境類擁有各類不一樣狀態的對象,做爲外部使用的接口,負責調用狀態類接口。加密
2)State(抽象狀態):抽象狀態既能夠爲抽象類,也能夠直接定義成接口。主要用於定義狀態抽象方法,具體實現由子類負責。
3)ConcreteState(具體狀態類):具體狀態類爲抽象狀態的實現者,不一樣的狀態類對應這不一樣的狀態,其內部實現也不相同。環境類中使用不一樣狀態的對象時,能實現不一樣的處理邏輯
應用實例
一、打籃球的時候運動員能夠有正常狀態、不正常狀態和超常狀態。
二、曾侯乙編鐘中,'鍾是抽象接口','鍾A'等是具體狀態,'曾侯乙編鐘'是具體環境(Context)。
一、封裝了轉換規則。
二、枚舉可能的狀態,在枚舉狀態以前須要肯定狀態種類。
三、將全部與某個狀態有關的行爲放到一個類中,而且能夠方便地增長新的狀態,只須要改變對象狀態便可改變對象的行爲。
四、容許狀態轉換邏輯與狀態對象合成一體,而不是某一個巨大的條件語句塊。
五、可讓多個環境對象共享一個狀態對象,從而減小系統中對象的個數。
一、狀態模式的使用必然會增長系統類和對象的個數。
二、狀態模式的結構與實現都較爲複雜,若是使用不當將致使程序結構和代碼的混亂。
三、狀態模式對"開閉原則"的支持並不太好,對於能夠切換狀態的狀態模式,增長新的狀態類須要修改那些負責狀態轉換的源代碼,不然沒法切換到新增狀態,並且修改某個狀態類的行爲也需修改對應類的源代碼。
使用場景
行爲隨狀態改變而改變的場景。
條件、分支語句的代替者。
如今咱們知道,狀態模式和策略模式的結構是類似的,但它們的意圖不一樣。讓咱們重溫一下它們的主要不一樣之處:
- 策略模式封裝了一組相關算法,它容許Client在運行時使用可互換的行爲;狀態模式幫助一個類在不一樣的狀態顯示不一樣的行爲。
- 狀態模式封裝了對象的狀態,而策略模式封裝算法或策略。由於狀態是跟對象密切相關的,它不能被重用;而經過從Context中分離出策略或算法,咱們能夠重用它們。
- 在狀態模式中,每一個狀態經過持有Context的引用,來實現狀態轉移;可是每一個策略都不持有Context的引用,它們只是被Context使用。
- 策略實現能夠做爲參數傳遞給使用它的對象,例如Collections.sort(),它的參數包含一個Comparator策略。另外一方面,狀態是Context對象本身的一部分,隨着時間的推移,Context對象從一個狀態轉移到另外一個狀態。
- 雖然它們都符合OCP原則,策略模式也符合SRP原則(單一職責原則),由於每一個策略都封裝本身的算法,且不依賴其餘策略。一個策略的改變,並不會致使其餘策略的變化。
- 另外一個理論上的不一樣:策略模式定義了對象「怎麼作」的部分。例如,排序對象怎麼對數據排序。狀態模式定義了對象「是什麼」和「何時作」的部分。例如,對象處於什麼狀態,何時處在某個特定的狀態。
- 狀態模式中很好的定義了狀態轉移的次序;而策略模式並沒有此須要:Client能夠自由的選擇任何策略。
- 一些常見的策略模式的例子是封裝算法,例如排序算法,加密算法或者壓縮算法。若是你看到你的代碼須要使用不一樣類型的相關算法,那麼考慮使用策略模式吧。而識別什麼時候使用狀態模式是很簡單的:若是你須要管理狀態和狀態轉移,但不想使用大量嵌套的條件語句,那麼就是它了。
- 最後但最重要的一個不一樣之處是,策略的改變由Client完成;而狀態的改變,由Context或狀態本身。
simple1包中主要是對風扇的開關狀態進行轉換,其實咱們是把狀態放在狀態類中進行按照固定的邏輯轉換,可是這種模式其實他不符合開閉原則,爲何了,由於一旦咱們發生新增、修改或者刪除狀態的時候,就須要修改狀態類中的狀態轉換。
public class Application {
public static void main(String[] args) {
Context context = new Context(new CloseLevelState());
context.right();
context.right();
context.right();
context.left();
context.right();
context.right();
}
}
複製代碼
抽象狀態
public interface LevelState {
/** * 左轉 * * @param context */
void left(Context context);
/** * 右轉 * * @param context */
void right(Context context);
/** * 當前檔位 * @return */
String info();
}
複製代碼
具體檔位狀態,我只列了2個,其餘的相似
@Slf4j
public class OneLevelState implements LevelState {
@Override
public void left(Context context) {
LevelState levelState = new CloseLevelState();
context.setLevelState(levelState);
log.info("風扇左轉到{}", levelState.info());
}
@Override
public void right(Context context) {
LevelState levelState = new TwoLevelState();
context.setLevelState(levelState);
log.info("風扇右轉到{}", levelState.info());
}
@Override
public String info() {
return "1檔";
}
}
複製代碼
@Slf4j
public class CloseLevelState implements LevelState {
@Override
public void left(Context context) {
LevelState levelState = new ForeLevelState();
context.setLevelState(levelState);
log.info("風扇左轉到{}", levelState.info());
}
@Override
public void right(Context context) {
LevelState levelState = new OneLevelState();
context.setLevelState(levelState);
log.info("風扇右轉到{}", levelState.info());
}
@Override
public String info() {
return "0檔";
}
}
複製代碼
真正的開關也就是上下文
@Data
@AllArgsConstructor
public class Context {
private LevelState levelState;
public void left() {
levelState.left(this);
}
public void right() {
levelState.right(this);
}
public String info() {
return levelState.info();
}
}
複製代碼
歡迎關注