java狀態模式例項解析

weixin_33866037發表於2018-05-31

狀態模式是一種常用的設計模式,常用於軟體系統中解決多種狀態下行為互異的情況。例如在音視訊播放器中多種狀態各自對應著不相同的處理,使用狀態機可以完美實現解耦。最近在做一個專案的重構,有使用到狀態模式,因此寫這篇文章分享一下。

1、狀態模式概述:

狀態模式(State Pattern):允許一個物件在其內部狀態改變時改變它的行為,物件看起來似乎修改了它的類。其別名為狀態物件(Object for States),狀態模式是一種物件行為型模式。《設計模式的藝術》

3344200-0407b24a011bc7fd.png
image.png

1)Context(環境類):環境類擁有各種不同狀態的物件,作為外部使用的介面,負責呼叫狀態類介面。
2)State(抽象狀態):抽象狀態既可以為抽象類,也可以直接定義成介面。主要用於定義狀態抽象方法,具體實現由子類負責。
3)ConcreteState(具體狀態類):具體狀態類為抽象狀態的實現者,不同的狀態類對應這不同的狀態,其內部實現也不相同。環境類中使用不同狀態的物件時,能實現不同的處理邏輯。

2、狀態模式示例:

示例為真實專案,一個即時通訊客戶端連線服務端網路的設計。客戶端連線伺服器時存在多種狀態,最初是通過if-else判斷來實現,程式碼耦合度比較高。使用狀態設計模式之後,程式碼清晰很多,耦合降低。類圖與上面的基本一樣,因此此處不再展示,直接上程式碼。

public interface ConnectState {

    void handleRequest();
}
public class InitState implements ConnectState{

    private ConnectMachine connectMachine;

    private boolean connectResult;

    public InitState(ConnectMachine connectMachine) {
        this.connectMachine = connectMachine;
    }

    @Override
    public void handleRequest() {
        doConnect();
        connectMachine.waitingConnect();
    }

    private void doConnect() {
        //connect server
        if (connectResult) {
            connectSuccess();
        } else {
            connectFailure();
        }
    }

    private void connectSuccess() {
        connectMachine.connectSuccess();
    }

    private void connectFailure() {
        connectMachine.connectFailure();
    }
}
public class ConnectingState implements ConnectState{

    private ConnectMachine connectMachine;

    public ConnectingState(ConnectMachine connectMachine) {
        this.connectMachine = connectMachine;
    }

    @Override
    public void handleRequest() {
        waitingConnect();
    }

    private void waitingConnect() {
        //loading and do something
    }
}
public class ConnectSuccessState implements ConnectState{

    private ConnectMachine connectMachine;

    public ConnectSuccessState(ConnectMachine connectMachine) {
        this.connectMachine = connectMachine;
    }

    @Override
    public void handleRequest() {
        connectSuccess();
    }

    private void connectSuccess() {
        //do something
    }
}
public class ConnectFailureState implements ConnectState{

    private ConnectMachine connectMachine;

    public ConnectFailureState(ConnectMachine connectMachine) {
        this.connectMachine = connectMachine;
    }

    @Override
    public void handleRequest() {
        connectFailure();
    }

    private void connectFailure() {
        //do something
    }
}
public class ReConnectingState implements ConnectState{

    private ConnectMachine connectMachine;

    public ReConnectingState(ConnectMachine connectMachine) {
        this.connectMachine = connectMachine;
    }

    @Override
    public void handleRequest() {
        reConnect();
    }

    private void reConnect() {
        //disconnect old connect and connect again
        connectMachine.disConnect();
        connectMachine.doConnect();
    }
}
public class DisconnectState implements ConnectState {

    private ConnectMachine connectMachine;

    public DisconnectState(ConnectMachine connectMachine) {
        this.connectMachine = connectMachine;
    }

    @Override
    public void handleRequest() {
        disConnect();
    }

    private void disConnect() {
        //do something
    }
}
public class ConnectMachine {

    private ConnectState initState;
    private ConnectState connectingState;
    private ConnectState reconnectState;
    private ConnectState disConnectState;
    private ConnectState connectSuccessState;
    private ConnectState connectFailureState;

    public ConnectMachine() {
        initState = new InitState(this);
        connectingState = new ConnectingState(this);
        reconnectState = new ReConnectingState(this);
        disConnectState = new DisconnectState(this);
        connectSuccessState = new ConnectSuccessState(this);
        connectFailureState = new ConnectFailureState(this);
    }

    public void doConnect() {
        initState.handleRequest();
    }

    public void waitingConnect() {
        connectingState.handleRequest();
    }

    public void reConnect() {
        reconnectState.handleRequest();
    }

    public void disConnect() {
        disConnectState.handleRequest();
    }

    public void connectSuccess() {
        connectSuccessState.handleRequest();
    }

    public void connectFailure() {
        connectFailureState.handleRequest();
    }
}

如果不使用狀態模式,所有的邏輯判斷全部寫在ConnectMachine類裡面,而且是通過大量if-else判斷實現,程式碼可讀性比較差。改成狀態模式之後,實現細節交給具體狀態類去實現,ConnectMachine只是提供給外部系統使用的介面,具體實現對於使用者來說是不可的。因為只是展示狀態模式的設計,使用的是回撥的方式,在專案中使用時可以引入Rxjava,響應式程式設計更加能體會到其中妙處。

3、狀態模式優缺點分析:

優點:

1)面向介面式程式設計,將實現細節巧妙封裝在各個不同的狀態類中,狀態轉換交給狀態類自己去實現,外部無需關心;
2)將由大量業務、大量邏輯判斷的程式碼去除,狀態類內部通過狀態的轉換實現相關邏輯,程式碼可讀性更好;

缺點:

1)增加新的狀態時會增加狀態類,而且在增加新的狀態類之後,環境類需要做相應的修改,不太符合開閉原則;

結束語

狀態模式的介紹就到這,雖然狀態模式在設計上能夠帶來很大優勢。但是也不能濫用,因為對於一些簡單的邏輯,隨意使用狀態模式會使得系統複雜度增加。如果對設計模式感興趣的同學,推薦讀一下《設計模式的藝術》這本書,寫得很實用。

相關文章