您好,登錄后才能下訂單哦!
原文地址 折騰Java設計模式之狀態模式
在狀態模式(State Pattern)中,類的行為是基于它的狀態改變的。這種類型的設計模式屬于行為型模式。在狀態模式中,我們創建表示各種狀態的對象和一個行為隨著狀態對象改變而改變的 context 對象。通俗點就是一個對象在內部狀態發生改變時改變它的行為。
意圖 允許對象在內部狀態發生改變時改變它的行為,對象看起來好像修改了它的類。
主要解決 對象的行為依賴于它的狀態(屬性),并且可以根據它的狀態改變而改變它的相關行為。
何時使用 代碼中包含大量與對象狀態有關的條件語句。
如何解決 將各種具體的狀態類抽象出來。
關鍵代碼 通常命令模式的接口中只有一個方法。而狀態模式的接口中有一個或者多個方法。而且,狀態模式的實現類的方法,一般返回值,或者是改變實例變量的值。也就是說,狀態模式一般和對象的狀態有關。實現類的方法有不同的功能,覆蓋接口中的方法。狀態模式和命令模式一樣,也可以用于消除 if...else 等條件選擇語句。
UML圖
cdn.xitu.io/2019/4/4/169e5f0b6131a6d3?w=1058&h=616&f=png&s=16512">
主要角色
1)Context(環境類):環境類擁有各種不同狀態的對象,作為外部使用的接口,負責調用狀態類接口。
2)State(抽象狀態):抽象狀態既可以為抽象類,也可以直接定義成接口。主要用于定義狀態抽象方法,具體實現由子類負責。
3)ConcreteState(具體狀態類):具體狀態類為抽象狀態的實現者,不同的狀態類對應這不同的狀態,其內部實現也不相同。環境類中使用不同狀態的對象時,能實現不同的處理邏輯
應用實例
1、打籃球的時候運動員可以有正常狀態、不正常狀態和超常狀態。
2、曾侯乙編鐘中,'鐘是抽象接口','鐘A'等是具體狀態,'曾侯乙編鐘'是具體環境(Context)。
1、封裝了轉換規則。
2、枚舉可能的狀態,在枚舉狀態之前需要確定狀態種類。
3、將所有與某個狀態有關的行為放到一個類中,并且可以方便地增加新的狀態,只需要改變對象狀態即可改變對象的行為。
4、允許狀態轉換邏輯與狀態對象合成一體,而不是某一個巨大的條件語句塊。
5、可以讓多個環境對象共享一個狀態對象,從而減少系統中對象的個數。
1、狀態模式的使用必然會增加系統類和對象的個數。
2、狀態模式的結構與實現都較為復雜,如果使用不當將導致程序結構和代碼的混亂。
3、狀態模式對"開閉原則"的支持并不太好,對于可以切換狀態的狀態模式,增加新的狀態類需要修改那些負責狀態轉換的源代碼,否則無法切換到新增狀態,而且修改某個狀態類的行為也需修改對應類的源代碼。
使用場景
行為隨狀態改變而改變的場景。
- 條件、分支語句的代替者。
現在我們知道,狀態模式和策略模式的結構是相似的,但它們的意圖不同。讓我們重溫一下它們的主要不同之處:
- 策略模式封裝了一組相關算法,它允許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();
}
}
Java中,狀態模式和策略模式的區別
狀態模式|菜鳥教程
歡迎關注
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。