行為型模式:狀態模式

来源:https://www.cnblogs.com/liebrother/archive/2019/03/14/10527850.html
-Advertisement-
Play Games

十一大行為型模式之八:狀態模式。 簡介 姓名 :狀態模式 英文名 :State Pattern 價值觀 :有啥事讓狀態我來維護 個人介紹 : Allow an object to alter its behavior when its internal state changes.The objec ...


景色

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

簡介

姓名 :狀態模式
英文名 :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,一起交流學習。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 今天需要做手機端的輸入表單自動生成器,突然就想到了手機端對input的輸入類型支持還不錯,於是翻遍了資料,有了下麵的使用方法,閑話少說,上正文: html5現在可以用的新input type類型一共有下麵類型 => <input type="email" /> <input type="url" / ...
  • 示例代碼: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>lightbox</title> <link href="css/lightbox.css" rel="stylesheet"> <script t ...
  • 示例代碼: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <link rel="stylesheet" href="../themes/default/default.cs ...
  • v-model用於數據的雙向綁定。bug不少,湊合看吧,主要是練習v-model。 ...
  • function myJquery(selector){ if(typeof selector=="string") { if (selector.charAt(0) == "<" && selector.slice(-1) == ">") { var ele = selector.slice(1, ...
  • 11111 ...
  • 該需求出現原因:想要實現一個在一開始載入頁面時就出現一行文字逐漸出現的效果,且需要實現的是一種逐漸的過渡出現效果為不是一種生硬的突然間歇性出現。於是便開始嘗試利用最近正在學習的jQuery技術和JS實現。 【註】:該篇文章適合初學者閱讀,大佬請跳過。 【需要的知識點】:JS中利用Timing中的兩個 ...
  • Vue組件通訊 組件可謂是 Vue框架的最有特色之一, 可以將一大塊拆分為小零件最後組裝起來。這樣的好處易於維護、擴展和復用等。 提到 Vue的組件, 相必大家對Vue組件之間的數據流並不陌生。最常規的是父組件的數據通過 prop傳遞給子組件。子組件要進行數據更新,那麼需要通過自定義事件通知父組件。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...