行為型模式:狀態模式

LieBrother發表於2019-03-13

景色

十一大行為型模式之八:狀態模式。

簡介

姓名 :狀態模式

英文名 :State Pattern

價值觀 :有啥事讓狀態我來維護

個人介紹

Allow an object to alter its behavior when its internal state changes.The object will appear to change its class.

當一個物件內在狀態改變時允許其改變行為,這個物件看起來像改變了其類。

(來自《設計模式之禪》)

你要的故事

現在有好多個人貸款軟體,比如:支付寶、360借條(打廣告。。。)等等。貸款會有一個使用者狀態流程,遊客->註冊使用者->授信使用者->借款使用者(這裡簡化了狀態,只用 4 個)。每個狀態擁有的許可權不一樣,如下圖所示。

狀態

從上圖可以看到,一個使用者有 3 種行為,分別是註冊、授信、借款。當註冊成功後,使用者的狀態就從『遊客』改變為『註冊使用者』;當授信成功後,使用者的狀態就從『註冊使用者』改變為『授信使用者』;當借款成功後,使用者的狀態就從『授信使用者』改變為『借款使用者』。現在我們就來實現使用者註冊、授信、借款的過程,因為每個狀態的許可權不一樣,所以這裡需要根據使用者的狀態來限制使用者行為。

很快,我們就完成下面的程式碼。

class User {
    private String state;

    public String getState() {
        return state;
    }

    public void setState(String state) {
        this.state = state;
    }

    public void register() {
        if ("none".equals(state)) {
            System.out.println("遊客。註冊中。。。");
        }else if ("register".equals(state)) {
            System.out.println("註冊使用者。不需要再註冊。");
        } else if ("apply".equals(state)) {
            System.out.println("授信使用者。不需要再註冊。");
        } else if ("draw".equals(state)) {
            System.out.println("借款使用者。不需要再註冊。");
        }
    }

    public void apply() {
        if ("none".equals(state)) {
            System.out.println("遊客。不能申請授信。");
        }else if ("register".equals(state)) {
            System.out.println("註冊使用者。授信申請中。。。");
        } else if ("apply".equals(state)) {
            System.out.println("授信使用者。不需要再授信。");
        } else if ("draw".equals(state)) {
            System.out.println("借款使用者。不需要再授信。");
        }
    }

    public void draw(double money) {
        if ("none".equals(state)) {
            System.out.println("遊客。申請借款【" + money + "】元。不能申請借款。");
        } else if ("register".equals(state)) {
            System.out.println("註冊使用者。申請借款【" + money + "】元。還沒授信,不能借款。");
        } else if ("apply".equals(state)) {
            System.out.println("授信使用者。申請借款【" + money + "】元。申請借款中。。。");
        } else if ("draw".equals(state)) {
            System.out.println("授信使用者。申請借款【" + money + "】元。申請借款中。。。");
        }
    }
}

public class NoStateTest {

    public static void main(String[] args) {
        User user = new User();
        user.setState("register");
        user.draw(1000);
    }

}

列印結果:
註冊使用者。申請借款【1000.0】元。還沒授信,不能借款。
複製程式碼

上面程式碼實現了使用者 register (註冊),apply (授信),draw (借款) 這 3 種行為,每個行為都會根據狀態 state 來做許可權控制。看起來有點繁瑣,擴充套件性不高,假設新增了一個狀態,那麼註冊、授信、借款這 3 種行為的程式碼都要修改。下面通過狀態模式來解決這個問題。

我們把狀態給抽出來,作為一個介面,因為在每種狀態中都可能有註冊、授信、借款行為,所以把這 3 個行為作為狀態介面的方法,讓每個狀態子類都實現相應的行為控制。如下程式碼所示。

interface State {

    void register();

    void apply();

    void draw(double money);
}

/**
 * 遊客
 */
class NoneState implements State {

    @Override
    public void register() {
        System.out.println("遊客。註冊中。。。");
    }

    @Override
    public void apply() {
        System.out.println("遊客。不能申請授信。");
    }

    @Override
    public void draw(double money) {
        System.out.println("遊客。申請借款【" + money + "】元。不能申請借款。");
    }
}

/**
 * 註冊狀態
 */
class RegisterState implements State {

    @Override
    public void register() {
        System.out.println("註冊使用者。不需要再註冊。");
    }

    @Override
    public void apply() {
        System.out.println("註冊使用者。授信申請中。。。");
    }

    @Override
    public void draw(double money) {
        System.out.println("註冊使用者。申請借款【" + money + "】元。還沒授信,不能借款。");
    }
}

/**
 * 授信狀態
 */
class ApplyState implements State {

    @Override
    public void register() {
        System.out.println("授信使用者。不需要再註冊。");
    }

    @Override
    public void apply() {
        System.out.println("授信使用者。不需要再授信。");
    }

    @Override
    public void draw(double money) {
        System.out.println("授信使用者。申請借款【" + money + "】元。申請借款中。。。");
    }
}

/**
 * 借款狀態
 */
class DrawState implements State {

    @Override
    public void register() {
        System.out.println("借款使用者。不需要再註冊。");
    }

    @Override
    public void apply() {
        System.out.println("借款使用者。不需要再授信。");
    }

    @Override
    public void draw(double money) {
        System.out.println("申請借款【" + money + "】元。申請借款中。。。");
    }
}

class User1 {
    private State state;

    public State getState() {
        return state;
    }

    public void setState(State state) {
        this.state = state;
    }

    public void register() {
        this.state.register();
    }

    public void apply() {
        this.state.apply();
    }

    public void draw(double money) {
        this.state.draw(money);
    }
}

public class StateTest {
    public static void main(String[] args) {
        User1 user1 = new User1();
        user1.setState(new RegisterState());
        user1.apply();
        user1.draw(1000);
        user1.setState(new ApplyState());
        user1.draw(2000);
    }

}


列印結果:
註冊使用者。授信申請中。。。
註冊使用者。申請借款【1000.0】元。還沒授信,不能借款。
授信使用者。申請借款【2000.0】元。申請借款中。。。
複製程式碼

看上面程式碼,我們抽象了 State 介面,4 種狀態分別用 NoneState (遊客)、RegisterState (註冊)、ApplyState (授信)、DrawState (借款) 表示。而每個狀態都有 3 種行為,它們各自對這些行為進行許可權控制。這樣子實現可以讓許可權邏輯分離開,分散到每個狀態裡面去,如果以後要業務擴充套件,要新增狀態,那就很方便了,只需要再實現一個狀態類就可以,不會影響到其他程式碼。這也是為什麼《阿里巴巴 Java 開發手冊》裡面講的,當超過 3 層的 if-else 的邏輯判斷程式碼,推薦用狀態模式來重構程式碼。

總結

狀態模式 很好的減低了程式碼的複雜性,從而提高了系統的可維護性。在業務開發中可以嘗試使用,比如在迭代開發中,業務邏輯越來越複雜,從而不得不使用很多 if-else 語句來實現時,就可以考慮一下是不是可以用 狀態模式 來重構,特別是一些有狀態流程轉換方面的業務。看到這篇文章,想想工作中是不是有些複雜的程式碼可以重構,趕緊行動起來。

推薦閱讀:

行為型模式:觀察者模式

行為型模式:迭代器模式

行為型模式:策略模式

設計模式系列文章持續更新中,歡迎關注公眾號 LieBrother,一起交流學習。

LieBrother

相關文章