設計模式之策略模式和狀態模式(strategy pattern & state pattern)

本文來說解一下兩個結構比較類似的行爲設計模式:策略模式和狀態模式。二者單獨的理解和學習都是比較直觀簡單的,可是實際使用的時候卻並很差實踐,算是易學難用的設計模式吧。這也是把二者放在一塊兒介紹的緣由,通過對比和實例介紹,相信應該會一些比較深入的感知。最後在結合我的的體會簡單聊一下對這兩個模式的一些見解。算法

1. 模式概念

1.1 策略模式

運行時更改類的行爲或算法,從而達到修改其功能的目的;設計模式

使用場景: 一個系統須要動態地在幾種算法中選擇一種,而這些算法之間僅僅是他們的行爲不一樣。 此外決策過程當中過多的出現if else,也能夠考慮使用該模式。api

實現:將這些算法封裝成可單獨運行的類,由使用者根據須要進行替換。ide

優勢: 較爲靈活,擴展性好,避免大量的if else結構。函數

缺點: 對外暴露了類全部的行爲和算法,行爲過多致使策略類膨脹。性能

1.2 狀態模式

運行時類的行爲由其狀態決定;學習

使用場景: 對象依賴裝填,行爲隨狀態改變而改變的情景,或者存在大量的if else和分支結構等;this

實現:將對象的狀態封裝成單個的類,每一個狀態處理該狀態下的事務,並控制該狀態到其餘狀態的轉移;加密

優勢: 容易新加狀態,封裝了狀態轉移規則,每一個狀態能夠被複用和共享,避免大量的if else結構。spa

缺點: 該模式結構和實現相對複雜,狀態過多致使增長類和對象個數。同時因爲由每一個狀態控制向其餘狀態的轉移,新加狀態必需要修改現有的部分狀態才能加入狀態機中生效。

1.3 相同點

二者經過將行爲和狀態拆分紅一系列小的組件,由條件和狀態進行功能更替,這樣符合開閉原則,便於擴展。此外都可做爲if else或者分支的替換方案;支持的最大行爲和狀態均有限;

1.4 不一樣點

  • 策略模式中,類的功能是根據當前條件主動更改;
  • 狀態模式中,類的功能是被動由當前狀態更改;
  • 策略模式中每一個行爲或算法之間沒有關聯;
  • 狀態模式中的狀態之間有關聯,而且狀態自己控制着狀態轉移;

2. 原理

兩種模式的結構很是類似,下面分別看一下兩種設計模式的UML類圖:

2.1 策略模式UML

描述:

Context:

使用了某種策略的類,其行爲由其包含的具體的策略決定,該類能主動修改使用的策略從而改變其行爲;

Strategy:

抽象策略類,用於定義全部支持的算法公共接口;

ConcreteStrategy:

可以被Context使用的具體的策略;

2.2 狀態模式UML

描述:

Context:

帶有某個狀態標記的類,其行爲由其當前的狀態決定,類狀態的轉移由狀態來控制;

State:

抽象狀態類,用於定義Context全部狀態的公共接口;

ConcreteState:

Context類的某種具體狀態,包含了該狀態下處理的事務並控制向他狀態轉移;

3. 實例——策略模式

舉個壓縮軟件使用不一樣壓縮策略的例子。

 

抽象策略接口:Compression

public interface Compression {
    public void doCompression();
}

快速壓縮算法:Rapid

public class Rapid implements Compression {
    @Override
    public void doCompression() {
        System.out.println("Use rapid compression strategy!");
    }
}

高效壓縮算法:Efficient

public class Efficient implements Compression {
    @Override
    public void doCompression() {
        System.out.println("Use efficient compression strategy!");
    }
}

加密壓縮算法:Encrypt

public class Encrypt implements Compression {
    @Override
    public void doCompression() {
        // TODO Auto-generated method stub
        System.out.println("Use encrypt compression strategy!");
    }
}

集成上面壓縮算法的軟件:WinRAR

public class WinRAR {
    
    private Compression compression = null;
    
    public WinRAR(Compression compression) {
        this.compression = compression;
    }
    
    public void setStrategy(Compression compression) {
        this.compression = compression;
    }
    
    public void compression() {
        if (compression != null) {
            compression.doCompression();
        }
    }
}

演示:

public class Demo {
    public static void main(String[] args) {
        WinRAR winrar = new WinRAR(new Rapid());
        winrar.compression();
        winrar.setStrategy(new Efficient());
        winrar.compression();
        winrar.setStrategy(new Encrypt());
        winrar.compression();
    }
}

結果:

Use rapid compression strategy!
Use efficient compression strategy!
Use encrypt compression strategy!

這個例子看着很直觀,後面會給出一點分析和我的的理解。

4. 實例——狀態模式

咱們經過自動洗衣機工做過程來描述一下狀態模式使用。

簡單起見,這裏咱們僅僅考慮【開始】-> 【工做】-> 【結束】,這三個狀態。

下面先來看一下其UML的類圖:

抽象狀態接口:State

public interface State {
    public void doJob(Washing washing);
}

開始狀態:Start

public class Start implements State {
    @Override
    public void doJob(Washing washing) {
        System.out.println("Start Washing Clothes!");
        washing.setState(new Work());
        washing.request();
    }
}

工做狀態:Work

public class Work implements State{
    @Override
    public void doJob(Washing washing) {
        System.out.println("Working Now!");
        washing.setState(new End());
        washing.request();
    }
}

結束狀態:End

public class End implements State{
    @Override
    public void doJob(Washing washing) {
        System.out.println("All Finished!");
        washing.setState(null);
    }
}

洗衣機類:Washing

public class Washing {
    private State state = null;
    
    public void setState(State state) {
        this.state = state;
        if (state == null) {
            System.out.println("Current state: null!");
        }
        else {
            System.out.println("Current state: " + state.getClass().getName());
        }
    }
    
    public void request() {
        if (state != null) {
            state.doJob(this);
        }
    }
}

演示:

public class Demo {
    public static void main(String[] args) {
        Washing washing = new Washing();
        washing.setState(new Start());
        washing.request();
    }
}

結果:

Current state: state.Start
Start Washing Clothes!
Current state: state.Work
Working Now!
Current state: state.End
All Finished!
Current state: null!

washing中提供用戶使用的主要接口。初始時,使用者使用一個狀態來配置washing,而後即可對washing發送指令,後續不在須要用戶直接於具體轉態打交道。每一個狀態會自動控制向下一個狀態轉移,直到運行結束。

5. 總結

談一下我的對於策略設計模式和狀態模式的一些理解(不必定對,僅僅是一些思考):

5.1 策略模式:

a)頻繁使用if else 可能嚴重消耗性能

策略模式比較適用於,行爲類常常在某一個模式下工做,而不是會根據隨機條件進行切換。

舉個例子,在APP開發過程當中,某一功能會依賴於橫豎屏狀態,那麼咱們是否須要在每一幀都是使用if else進行判斷當前是橫屏仍是豎屏,而後進行下一步的處理?

顯然這會嚴重消耗性能,正確的作法是將橫豎屏處理拆分紅兩個策略,每次屏幕切換的時候,主動的切一下使用的模式;

b) 並非全部的if else 和 分支均可以使用策略模式來替代

對於上面的壓縮軟件的例子,用戶會選用一種模式,而後進行下面的工做,這個沒問題。

可是若是咱們提供的是一個壓縮命令,該命令能夠根據傳遞的參數,使用不一樣的壓縮方式,那麼使用if else就是必要的,由於咱們不知道用戶會輸入什麼參數,使用什麼模式。

c) 策略模式沒有策略

策略模式的核心是將一系列的操做拆分紅可若干可單獨重複使用的輪子,特定條件下直接選取其中一個使用,而不是傳遞條件,使用if else來進行條件判斷以執行相應的操做。

從這個角度來看,策略模式名存實亡,其不只沒有智能,合理的根據當前條件進行決策,還須要使用者主動的選取一種策略進行執行。這樣作有好處,但同時其也變得更加沒有策略。

實際開發過程當中,咱們都但願對方提供的接口簡單好用,最好一個接口能搞定全部的問題,由於對於調用者而言,我並不關心你的實現,我只關心簡單使用這個接口完成個人需求。

根本緣由在於其破壞了封裝性,暴露了具體的策略,這是其拆分組件便於擴展的同時帶來的一個不可迴避的問題,策略模式將決策由執行者提早到了調用者,代碼靈活可擴展的同時帶來的是使用的不便。

若是說策略模式主要是爲了不大量的if else決策,那麼語言支持的話徹底可使用hashtable,分別以條件和函數對象做爲key,value來直接根據條件選取對應的操做。對於大量分支尤爲適用。

所以實際開發過程當中須要根據本身的實際狀況權衡利弊。

5.1 策略模式:

狀態模式的核心是將對象每個狀態作的事情分別交給每個單獨的狀態對象處理,而且由狀態本身控制向其餘狀態的轉移;行爲類僅向外提供方便用戶使用的接口;

對擴展狀態不是特別友好,須要修改其餘狀態的轉移。其次其實現比較靈活,用很差容易出錯。

小結:

策略模式是經過 Context 自己的決策來主動更替使用的strategy對象達到改變行爲的目的,狀態模式經過狀態轉移來被動的更改當前的State對象,狀態的改變發生在運行時。

策略模式提早封裝一組能夠互相替代的算法族,根據須要動態的選擇合適的一個來處理問題,而狀態模式處理不一樣狀態下, Context 對象行爲不一樣的問題;

相關文章
相關標籤/搜索