日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

重學(xué) Java 設(shè)計(jì)模式:實(shí)戰(zhàn)狀態(tài)模式「模擬系統(tǒng)營銷活動,狀態(tài)流程審核發(fā)布上線場景」

 小傅哥 2021-12-13


作者:小傅哥
博客:https:// - 原創(chuàng)系列專題文章

沉淀、分享、成長,讓自己和他人都能有所收獲!😄

文章目錄

一、前言

寫好代碼三個(gè)關(guān)鍵點(diǎn)

如果把寫代碼想象成家里的軟裝,你肯定會想到家里需要有一個(gè)非常不錯(cuò)格局最好是南北通透的,買回來的家具最好是品牌保證質(zhì)量的,之后呢是大小合適,不能擺放完了看著別扭。那么把這一過程抽象成寫代碼就是需要三個(gè)核心的關(guān)鍵點(diǎn);架構(gòu)(房間的格局)、命名(品牌和質(zhì)量)、注釋(尺寸大小說明書),只有這三個(gè)點(diǎn)都做好才能完成出一套賞心悅目的

平原走碼🐎易放難收

上學(xué)期間你寫了多少代碼?上班一年你能寫多少代碼?回家自己學(xué)習(xí)寫了多少代碼?個(gè)人素養(yǎng)的技術(shù)棧地基都是一塊一塊磚碼出來的,寫的越廣越深,根基就越牢固。當(dāng)根基牢固了以后在再上層建設(shè)就變得迎刃而解了,也更容易建設(shè)了。往往最難的就是一層一層階段的突破,突破就像破殼一樣,也像夯實(shí)地基,短時(shí)間看不到成績,也看不出高度。但以后誰能走的穩(wěn),就靠著默默的沉淀。

技術(shù)傳承的重要性

可能是現(xiàn)在時(shí)間節(jié)奏太快,一個(gè)需求下來恨不得當(dāng)天就上線(這個(gè)需求很簡單,怎么實(shí)現(xiàn)我不管,明天上線!),導(dǎo)致團(tuán)隊(duì)的人都很慌很急、很累、很崩潰,最終反反復(fù)復(fù)的人員更替,項(xiàng)目在這個(gè)過程中也交接了N次,文檔不全、代碼混亂、錯(cuò)綜復(fù)雜,誰在后面接手也都只能修修補(bǔ)補(bǔ),就像爛尾樓。這個(gè)沒有傳承、沒有沉淀的項(xiàng)目,很難跟隨業(yè)務(wù)的發(fā)展。最終!根基不牢,一地雞毛。

二、開發(fā)環(huán)境

  1. JDK 1.8
  2. Idea + Maven
  3. 涉及工程三個(gè),可以通過關(guān)注公眾號bugstack蟲洞棧,回復(fù)源碼下載獲取(打開獲取的鏈接,找到序號18)
工程描述
itstack-demo-design-19-00場景模擬工程;模擬營銷活動操作服務(wù)(查詢、審核)
itstack-demo-design-19-01使用一坨代碼實(shí)現(xiàn)業(yè)務(wù)需求
itstack-demo-design-19-02通過設(shè)計(jì)模式優(yōu)化改造代碼,產(chǎn)生對比性從而學(xué)習(xí)

三、狀態(tài)模式介紹

狀態(tài)模式,圖片來自 refactoringguru.cn

狀態(tài)模式描述的是一個(gè)行為下的多種狀態(tài)變更,比如我們最常見的一個(gè)網(wǎng)站的頁面,在你登錄與不登錄下展示的內(nèi)容是略有差異的(不登錄不能展示個(gè)人信息),而這種登錄不登錄就是我們通過改變狀態(tài),而讓整個(gè)行為發(fā)生了變化。

收音機(jī)&放音機(jī)&磁帶機(jī)

至少80后、90后的小伙伴基本都用過這種磁帶放音機(jī)(可能沒有這個(gè)好看),它的上面是一排按鈕,當(dāng)放入磁帶后,通過上面的按鈕就可以讓放音機(jī)播放磁帶上的內(nèi)容(listen to 英語聽力考試),而且有些按鈕是互斥的,當(dāng)在某個(gè)狀態(tài)下才可以按另外的按鈕(這在設(shè)計(jì)模式里也是一個(gè)關(guān)鍵的點(diǎn))。

四、案例場景模擬

在本案例中我們模擬營銷活動審核狀態(tài)流轉(zhuǎn)場景(一個(gè)活動的上線是多個(gè)層級審核上線的)

在上圖中也可以看到我們的流程節(jié)點(diǎn)中包括了各個(gè)狀態(tài)到下一個(gè)狀態(tài)扭轉(zhuǎn)的關(guān)聯(lián)條件,比如;審核通過才能到活動中,而不能從編輯中直接到活動中,而這些狀態(tài)的轉(zhuǎn)變就是我們要完成的場景處理。

大部分程序員基本都開發(fā)過類似的業(yè)務(wù)場景,需要對活動或者一些配置需要審核后才能對外發(fā)布,而這個(gè)審核的過程往往會隨著系統(tǒng)的重要程度而設(shè)立多級控制,來保證一個(gè)活動可以安全上線,避免造成資損。

當(dāng)然有時(shí)候會用到一些審批流的過程配置,也是非常方便開發(fā)類似的流程的,也可以在配置中設(shè)定某個(gè)節(jié)點(diǎn)的審批人員。但這不是我們主要體現(xiàn)的點(diǎn),在本案例中我們主要是模擬學(xué)習(xí)對一個(gè)活動的多個(gè)狀態(tài)節(jié)點(diǎn)的審核控制。

1. 場景模擬工程

itstack-demo-design-19-00
└── src
    └── main
        └── java
            └── org.itstack.demo.design
                ├── ActivityInfo.java
                ├── Status.java
                └── ActivityService.java
  • 在這個(gè)模擬工程里我們提供了三個(gè)類,包括;狀態(tài)枚舉(Status)、活動對象(ActivityInfo)、活動服務(wù)(ActivityService),三個(gè)服務(wù)類。
  • 接下來我們就分別介紹三個(gè)類包括的內(nèi)容。

2. 代碼實(shí)現(xiàn)

2.1 基本活動信息

public class ActivityInfo {

    private String activityId;    // 活動ID
    private String activityName;  // 活動名稱
    private Enum<Status> status;  // 活動狀態(tài)
    private Date beginTime;       // 開始時(shí)間
    private Date endTime;         // 結(jié)束時(shí)間
   
    // ...get/set
}  
  • 一些基本的活動信息;活動ID、活動名稱、活動狀態(tài)、開始時(shí)間、結(jié)束時(shí)間。

2.2 活動枚舉狀態(tài)

public enum Status {

    // 1創(chuàng)建編輯、2待審核、3審核通過(任務(wù)掃描成活動中)、4審核拒絕(可以撤審到編輯狀態(tài))、5活動中、6活動關(guān)閉、7活動開啟(任務(wù)掃描成活動中)
    Editing, Check, Pass, Refuse, Doing, Close, Open

}
  • 活動的枚舉;1創(chuàng)建編輯、2待審核、3審核通過(任務(wù)掃描成活動中)、4審核拒絕(可以撤審到編輯狀態(tài))、5活動中、6活動關(guān)閉、7活動開啟(任務(wù)掃描成活動中)

2.3 活動服務(wù)接口

public class ActivityService {

    private static Map<String, Enum<Status>> statusMap = new ConcurrentHashMap<String, Enum<Status>>();

    public static void init(String activityId, Enum<Status> status) {
        // 模擬查詢活動信息
        ActivityInfo activityInfo = new ActivityInfo();
        activityInfo.setActivityId(activityId);
        activityInfo.setActivityName("早起學(xué)習(xí)打卡領(lǐng)獎(jiǎng)活動");
        activityInfo.setStatus(status);
        activityInfo.setBeginTime(new Date());
        activityInfo.setEndTime(new Date());
        statusMap.put(activityId, status);
    }

    /**
     * 查詢活動信息
     *
     * @param activityId 活動ID
     * @return 查詢結(jié)果
     */
    public static ActivityInfo queryActivityInfo(String activityId) {
        // 模擬查詢活動信息
        ActivityInfo activityInfo = new ActivityInfo();
        activityInfo.setActivityId(activityId);
        activityInfo.setActivityName("早起學(xué)習(xí)打卡領(lǐng)獎(jiǎng)活動");
        activityInfo.setStatus(statusMap.get(activityId));
        activityInfo.setBeginTime(new Date());
        activityInfo.setEndTime(new Date());
        return activityInfo;
    }

    /**
     * 查詢活動狀態(tài)
     *
     * @param activityId 活動ID
     * @return 查詢結(jié)果
     */
    public static Enum<Status> queryActivityStatus(String activityId) {
        return statusMap.get(activityId);
    }

    /**
     * 執(zhí)行狀態(tài)變更
     *
     * @param activityId   活動ID
     * @param beforeStatus 變更前狀態(tài)
     * @param afterStatus  變更后狀態(tài) b
     */
    public static synchronized void execStatus(String activityId, Enum<Status> beforeStatus, Enum<Status> afterStatus) {
        if (!beforeStatus.equals(statusMap.get(activityId))) return;
        statusMap.put(activityId, afterStatus);
    }

}
  • 在這個(gè)靜態(tài)類中提供了活動的查詢和狀態(tài)變更接口;queryActivityInfo、queryActivityStatus、execStatus
  • 同時(shí)使用Map的結(jié)構(gòu)來記錄活動ID和狀態(tài)變化信息,另外還有init方法來初始化活動數(shù)據(jù)。實(shí)際的開發(fā)中這類信息基本都是從數(shù)據(jù)庫或者Redis中獲取。

五、用一坨坨代碼實(shí)現(xiàn)

這里我們先使用最粗暴的方式來實(shí)現(xiàn)功能

對于這樣各種狀態(tài)的變更,最讓我們直接想到的就是使用ifelse進(jìn)行判斷處理。每一個(gè)狀態(tài)可以流轉(zhuǎn)到下一個(gè)什么狀態(tài),都可以使用嵌套的if實(shí)現(xiàn)。

1. 工程結(jié)構(gòu)

itstack-demo-design-19-01
└── src
    └── main
        └── java
            └── org.itstack.demo.design
                ├── ActivityExecStatusController.java
                └── Result.java
  • 整個(gè)實(shí)現(xiàn)的工程結(jié)構(gòu)比較簡單,只包括了兩個(gè)類;ActivityExecStatusControllerResult,一個(gè)是處理流程狀態(tài),另外一個(gè)是返回的對象。

2. 代碼實(shí)現(xiàn)

public class ActivityExecStatusController {

    /**
     * 活動狀態(tài)變更
     * 1. 編輯中 -> 提審、關(guān)閉
     * 2. 審核通過 -> 拒絕、關(guān)閉、活動中
     * 3. 審核拒絕 -> 撤審、關(guān)閉
     * 4. 活動中 -> 關(guān)閉
     * 5. 活動關(guān)閉 -> 開啟
     * 6. 活動開啟 -> 關(guān)閉
     *
     * @param activityId   活動ID
     * @param beforeStatus 變更前狀態(tài)
     * @param afterStatus  變更后狀態(tài)
     * @return 返回結(jié)果
     */
    public Result execStatus(String activityId, Enum<Status> beforeStatus, Enum<Status> afterStatus) {

        // 1. 編輯中 -> 提審、關(guān)閉
        if (Status.Editing.equals(beforeStatus)) {
            if (Status.Check.equals(afterStatus) || Status.Close.equals(afterStatus)) {
                ActivityService.execStatus(activityId, beforeStatus, afterStatus);
                return new Result("0000", "變更狀態(tài)成功");
            } else {
                return new Result("0001", "變更狀態(tài)拒絕");
            }
        }

        // 2. 審核通過 -> 拒絕、關(guān)閉、活動中
        if (Status.Pass.equals(beforeStatus)) {
            if (Status.Refuse.equals(afterStatus) || Status.Doing.equals(afterStatus) || Status.Close.equals(afterStatus)) {
                ActivityService.execStatus(activityId, beforeStatus, afterStatus);
                return new Result("0000", "變更狀態(tài)成功");
            } else {
                return new Result("0001", "變更狀態(tài)拒絕");
            }
        }

        // 3. 審核拒絕 -> 撤審、關(guān)閉
        if (Status.Refuse.equals(beforeStatus)) {
            if (Status.Editing.equals(afterStatus) || Status.Close.equals(afterStatus)) {
                ActivityService.execStatus(activityId, beforeStatus, afterStatus);
                return new Result("0000", "變更狀態(tài)成功");
            } else {
                return new Result("0001", "變更狀態(tài)拒絕");
            }
        }

        // 4. 活動中 -> 關(guān)閉
        if (Status.Doing.equals(beforeStatus)) {
            if (Status.Close.equals(afterStatus)) {
                ActivityService.execStatus(activityId, beforeStatus, afterStatus);
                return new Result("0000", "變更狀態(tài)成功");
            } else {
                return new Result("0001", "變更狀態(tài)拒絕");
            }
        }

        // 5. 活動關(guān)閉 -> 開啟
        if (Status.Close.equals(beforeStatus)) {
            if (Status.Open.equals(afterStatus)) {
                ActivityService.execStatus(activityId, beforeStatus, afterStatus);
                return new Result("0000", "變更狀態(tài)成功");
            } else {
                return new Result("0001", "變更狀態(tài)拒絕");
            }
        }

        // 6. 活動開啟 -> 關(guān)閉
        if (Status.Open.equals(beforeStatus)) {
            if (Status.Close.equals(afterStatus)) {
                ActivityService.execStatus(activityId, beforeStatus, afterStatus);
                return new Result("0000", "變更狀態(tài)成功");
            } else {
                return new Result("0001", "變更狀態(tài)拒絕");
            }
        }

        return new Result("0001", "非可處理的活動狀態(tài)變更");

    }

}
  • 這里我們只需要看一下代碼實(shí)現(xiàn)的結(jié)構(gòu)即可。從上到下是一整篇的ifelse,基本這也是大部分初級程序員的開發(fā)方式。
  • 這樣的面向過程式開發(fā)方式,對于不需要改動代碼,也不需要二次迭代的,還是可以使用的(但基本不可能不迭代)。而且隨著狀態(tài)和需求變化,會越來越難以維護(hù),后面的人也不好看懂并且很容易填充其他的流程進(jìn)去。越來越亂就是從點(diǎn)滴開始的

3. 測試驗(yàn)證

3.1 編寫測試類

@Test
public void test() {
    // 初始化數(shù)據(jù)
    String activityId = "100001";
    ActivityService.init(activityId, Status.Editing);  

    ActivityExecStatusController activityExecStatusController = new ActivityExecStatusController();
    Result resultRefuse = activityExecStatusController.execStatus(activityId, Status.Editing, Status.Refuse); 
    logger.info("測試結(jié)果(編輯中To審核拒絕):{}", JSON.toJSONString(resultRefuse));                           

    Result resultCheck = activityExecStatusController.execStatus(activityId, Status.Editing, Status.Check);
    logger.info("測試結(jié)果(編輯中To提交審核):{}", JSON.toJSONString(resultCheck));
}
  • 我們的測試代碼包括了兩個(gè)功能的驗(yàn)證,一個(gè)是從編輯中審核拒絕,另外一個(gè)是從編輯中到提交審核
  • 因?yàn)閺奈覀兊膱鼍傲鞒讨锌梢钥吹?#xff0c;編輯中的活動是不能直接到審核拒絕的,這中間還需要提審。

3.2 測試結(jié)果

23:24:30.774 [main] INFO  org.itstack.demo.design.test.ApiTest - 測試結(jié)果(編輯中To審核拒絕){"code":"0001","info":"變更狀態(tài)拒絕"}
23:24:30.778 [main] INFO  org.itstack.demo.design.test.ApiTest - 測試結(jié)果(編輯中To提交審核){"code":"0000","info":"變更狀態(tài)成功"}

Process finished with exit code 0
  • 從測試結(jié)果和我們的狀態(tài)流程的流轉(zhuǎn)中可以看到,是符合測試結(jié)果預(yù)期的。除了不好維護(hù)外,這樣的開發(fā)過程還是蠻快的,但不建議這么搞!

六、狀態(tài)模式重構(gòu)代碼

接下來使用狀態(tài)模式來進(jìn)行代碼優(yōu)化,也算是一次很小的重構(gòu)。

重構(gòu)的重點(diǎn)往往是處理掉ifelse,而想處理掉ifelse基本離不開接口抽象類,另外還需要重新改造代碼結(jié)構(gòu)。

1. 工程結(jié)構(gòu)

itstack-demo-design-19-02
└── src
    └── main
        └── java
            └── org.itstack.demo.design
                ├── event
                │    ├── CheckState.java
                │    └── CloseState.java
                │    └── DoingState.java
                │    └── EditingState.java
                │    └── OpenState.java
                │    └── PassState.java
                │    └── RefuseState.java
                ├── Result.java
                ├── State.java
                └── StateHandler.java

狀態(tài)模式模型結(jié)構(gòu)

狀態(tài)模式模型結(jié)構(gòu)

  • 以上是狀態(tài)模式的整個(gè)工程結(jié)構(gòu)模型,State是一個(gè)抽象類,定義了各種操作接口(提審、審核、拒審等)。
  • 右側(cè)的不同顏色狀態(tài)與我們場景模擬中的顏色保持一致,是各種狀態(tài)流程流轉(zhuǎn)的實(shí)現(xiàn)操作。這里的實(shí)現(xiàn)有一個(gè)關(guān)鍵點(diǎn)就是每一種狀態(tài)到下一個(gè)狀態(tài),都分配到各個(gè)實(shí)現(xiàn)方法中控制,也就不需要if語言進(jìn)行判斷了。
  • 最后是StateHandler對狀態(tài)流程的統(tǒng)一處理,里面提供Map結(jié)構(gòu)的各項(xiàng)服務(wù)接口調(diào)用,也就避免了使用if判斷各項(xiàng)狀態(tài)轉(zhuǎn)變的流程。

2. 代碼實(shí)現(xiàn)

2.1 定義狀態(tài)抽象類

public abstract class State {

    /**
     * 活動提審
     *
     * @param activityId    活動ID
     * @param currentStatus 當(dāng)前狀態(tài)
     * @return 執(zhí)行結(jié)果
     */
    public abstract Result arraignment(String activityId, Enum<Status> currentStatus);

    /**
     * 審核通過
     *
     * @param activityId    活動ID
     * @param currentStatus 當(dāng)前狀態(tài)
     * @return 執(zhí)行結(jié)果
     */
    public abstract Result checkPass(String activityId, Enum<Status> currentStatus);

    /**
     * 審核拒絕
     *
     * @param activityId    活動ID
     * @param currentStatus 當(dāng)前狀態(tài)
     * @return 執(zhí)行結(jié)果
     */
    public abstract Result checkRefuse(String activityId, Enum<Status> currentStatus);

    /**
     * 撤審撤銷
     *
     * @param activityId    活動ID
     * @param currentStatus 當(dāng)前狀態(tài)
     * @return 執(zhí)行結(jié)果
     */
    public abstract Result checkRevoke(String activityId, Enum<Status> currentStatus);

    /**
     * 活動關(guān)閉
     *
     * @param activityId    活動ID
     * @param currentStatus 當(dāng)前狀態(tài)
     * @return 執(zhí)行結(jié)果
     */
    public abstract Result close(String activityId, Enum<Status> currentStatus);

    /**
     * 活動開啟
     *
     * @param activityId    活動ID
     * @param currentStatus 當(dāng)前狀態(tài)
     * @return 執(zhí)行結(jié)果
     */
    public abstract Result open(String activityId, Enum<Status> currentStatus);

    /**
     * 活動執(zhí)行
     *
     * @param activityId    活動ID
     * @param currentStatus 當(dāng)前狀態(tài)
     * @return 執(zhí)行結(jié)果
     */
    public abstract Result doing(String activityId, Enum<Status> currentStatus);

}
  • 在整個(gè)接口中提供了各項(xiàng)狀態(tài)流轉(zhuǎn)服務(wù)的接口,例如;活動提審、審核通過、審核拒絕、撤審撤銷等7個(gè)方法。
  • 在這些方法中所有的入?yún)⒍际且粯拥?#xff0c;activityId(活動ID)、currentStatus(當(dāng)前狀態(tài)),只有他們的具體實(shí)現(xiàn)是不同的。

2.2 部分狀態(tài)流轉(zhuǎn)實(shí)現(xiàn)

編輯

public class EditingState extends State {

    public Result arraignment(String activityId, Enum<Status> currentStatus) {
        ActivityService.execStatus(activityId, currentStatus, Status.Check);
        return new Result("0000", "活動提審成功");
    }

    public Result checkPass(String activityId, Enum<Status> currentStatus) {
        return new Result("0001", "編輯中不可審核通過");
    }

    public Result checkRefuse(String activityId, Enum<Status> currentStatus) {
        return new Result("0001", "編輯中不可審核拒絕");
    }

    @Override
    public Result checkRevoke(String activityId, Enum<Status> currentStatus) {
        return new Result("0001", "編輯中不可撤銷審核");
    }

    public Result close(String activityId, Enum<Status> currentStatus) {
        ActivityService.execStatus(activityId, currentStatus, Status.Close);
        return new Result("0000", "活動關(guān)閉成功");
    }

    public Result open(String activityId, Enum<Status> currentStatus) {
        return new Result("0001", "非關(guān)閉活動不可開啟");
    }

    public Result doing(String activityId, Enum<Status> currentStatus) {
        return new Result("0001", "編輯中活動不可執(zhí)行活動中變更");
    }

}

提審

public class CheckState extends State {

    public Result arraignment(String activityId, Enum<Status> currentStatus) {
        return new Result("0001", "待審核狀態(tài)不可重復(fù)提審");
    }

    public Result checkPass(String activityId, Enum<Status> currentStatus) {
        ActivityService.execStatus(activityId, currentStatus, Status.Pass);
        return new Result("0000", "活動審核通過完成");
    }

    public Result checkRefuse(String activityId, Enum<Status> currentStatus) {
        ActivityService.execStatus(activityId, currentStatus, Status.Refuse);
        return new Result("0000", "活動審核拒絕完成");
    }

    @Override
    public Result checkRevoke(String activityId, Enum<Status> currentStatus) {
        ActivityService.execStatus(activityId, currentStatus, Status.Editing);
        return new Result("0000", "活動審核撤銷回到編輯中");
    }

    public Result close(String activityId, Enum<Status> currentStatus) {
        ActivityService.execStatus(activityId, currentStatus, Status.Close);
        return new Result("0000", "活動審核關(guān)閉完成");
    }

    public Result open(String activityId, Enum<Status> currentStatus) {
        return new Result("0001", "非關(guān)閉活動不可開啟");
    }

    public Result doing(String activityId, Enum<Status> currentStatus) {
        return new Result("0001", "待審核活動不可執(zhí)行活動中變更");
    }

}
  • 這里提供了兩個(gè)具體實(shí)現(xiàn)類的內(nèi)容,編輯狀態(tài)和提審狀態(tài)。
  • 例如在這兩個(gè)實(shí)現(xiàn)類中,checkRefuse這個(gè)方法對于不同的類中有不同的實(shí)現(xiàn),也就是不同狀態(tài)下能做的下一步流轉(zhuǎn)操作已經(jīng)可以在每一個(gè)方法中具體控制了。
  • 其他5個(gè)類的操作是類似的具體就不在這里演示了,大部分都是重復(fù)代碼??梢酝ㄟ^源碼進(jìn)行學(xué)習(xí)理解。

2.3 狀態(tài)處理服務(wù)

public class StateHandler {

    private Map<Enum<Status>, State> stateMap = new ConcurrentHashMap<Enum<Status>, State>();

    public StateHandler() {
        stateMap.put(Status.Check, new CheckState());     // 待審核
        stateMap.put(Status.Close, new CloseState());     // 已關(guān)閉
        stateMap.put(Status.Doing, new DoingState());     // 活動中
        stateMap.put(Status.Editing, new EditingState()); // 編輯中
        stateMap.put(Status.Open, new OpenState());       // 已開啟
        stateMap.put(Status.Pass, new PassState());       // 審核通過
        stateMap.put(Status.Refuse, new RefuseState());   // 審核拒絕
    }

    public Result arraignment(String activityId, Enum<Status> currentStatus) {
        return stateMap.get(currentStatus).arraignment(activityId, currentStatus);
    }

    public Result checkPass(String activityId, Enum<Status> currentStatus) {
        return stateMap.get(currentStatus).checkPass(activityId, currentStatus);
    }

    public Result checkRefuse(String activityId, Enum<Status> currentStatus) {
        return stateMap.get(currentStatus).checkRefuse(activityId, currentStatus);
    }

    public Result checkRevoke(String activityId, Enum<Status> currentStatus) {
        return stateMap.get(currentStatus).checkRevoke(activityId, currentStatus);
    }

    public Result close(String activityId, Enum<Status> currentStatus) {
        return stateMap.get(currentStatus).close(activityId, currentStatus);
    }

    public Result open(String activityId, Enum<Status> currentStatus) {
        return stateMap.get(currentStatus).open(activityId, currentStatus);
    }

    public Result doing(String activityId, Enum<Status> currentStatus) {
        return stateMap.get(currentStatus).doing(activityId, currentStatus);
    }
    
}
  • 這是對狀態(tài)服務(wù)的統(tǒng)一控制中心,可以看到在構(gòu)造函數(shù)中提供了所有狀態(tài)和實(shí)現(xiàn)的具體關(guān)聯(lián),放到Map數(shù)據(jù)結(jié)構(gòu)中。
  • 同時(shí)提供了不同名稱的接口操作類,讓外部調(diào)用方可以更加容易的使用此項(xiàng)功能接口,而不需要像在itstack-demo-design-19-01例子中還得傳兩個(gè)狀態(tài)來判斷。

3. 測試驗(yàn)證

3.1 編寫測試類(Editing2Arraignment)

@Test
public void test_Editing2Arraignment() {
    String activityId = "100001";
    ActivityService.init(activityId, Status.Editing);
    StateHandler stateHandler = new StateHandler();
    Result result = stateHandler.arraignment(activityId, Status.Editing);
    logger.info("測試結(jié)果(編輯中To提審活動):{}", JSON.toJSONString(result));
    logger.info("活動信息:{} 狀態(tài):{}", JSON.toJSONString(ActivityService.queryActivityInfo(activityId)), JSON.toJSONString(ActivityService.queryActivityInfo(activityId).getStatus()));
}

測試結(jié)果

23:59:20.883 [main] INFO  org.itstack.demo.design.test.ApiTest - 測試結(jié)果(編輯中To提審活動){"code":"0000","info":"活動提審成功"}
23:59:20.907 [main] INFO  org.itstack.demo.design.test.ApiTest - 活動信息:{"activityId":"100001","activityName":"早起學(xué)習(xí)打卡領(lǐng)獎(jiǎng)活動","beginTime":1593694760892,"endTime":1593694760892,"status":"Check"} 狀態(tài):"Check"

Process finished with exit code 0
  • 測試編輯中To提審活動,的狀態(tài)流轉(zhuǎn)。

3.2 編寫測試類(Editing2Open)

@Test
public void test_Editing2Open() {
    String activityId = "100001";
    ActivityService.init(activityId, Status.Editing);
    StateHandler stateHandler = new StateHandler();
    Result result = stateHandler.open(activityId, Status.Editing);
    logger.info("測試結(jié)果(編輯中To開啟活動):{}", JSON.toJSONString(result));
    logger.info("活動信息:{} 狀態(tài):{}", JSON.toJSONString(ActivityService.queryActivityInfo(activityId)), JSON.toJSONString(ActivityService.queryActivityInfo(activityId).getStatus()));
}

測試結(jié)果

23:59:36.904 [main] INFO  org.itstack.demo.design.test.ApiTest - 測試結(jié)果(編輯中To開啟活動){"code":"0001","info":"非關(guān)閉活動不可開啟"}
23:59:36.914 [main] INFO  org.itstack.demo.design.test.ApiTest - 活動信息:{"activityId":"100001","activityName":"早起學(xué)習(xí)打卡領(lǐng)獎(jiǎng)活動","beginTime":1593694776907,"endTime":1593694776907,"status":"Editing"} 狀態(tài):"Editing"

Process finished with exit code 0
  • 測試編輯中To開啟活動,的狀態(tài)流轉(zhuǎn)。

3.3 編寫測試類(Refuse2Doing)

@Test
public void test_Refuse2Doing() {
    String activityId = "100001";
    ActivityService.init(activityId, Status.Refuse);
    StateHandler stateHandler = new StateHandler();
    Result result = stateHandler.doing(activityId, Status.Refuse);
    logger.info("測試結(jié)果(拒絕To活動中):{}", JSON.toJSONString(result));
    logger.info("活動信息:{} 狀態(tài):{}", JSON.toJSONString(ActivityService.queryActivityInfo(activityId)), JSON.toJSONString(ActivityService.queryActivityInfo(activityId).getStatus()));
}

測試結(jié)果

23:59:46.339 [main] INFO  org.itstack.demo.design.test.ApiTest - 測試結(jié)果(拒絕To活動中){"code":"0001","info":"審核拒絕不可執(zhí)行活動為進(jìn)行中"}
23:59:46.352 [main] INFO  org.itstack.demo.design.test.ApiTest - 活動信息:{"activityId":"100001","activityName":"早起學(xué)習(xí)打卡領(lǐng)獎(jiǎng)活動","beginTime":1593694786342,"endTime":1593694786342,"status":"Refuse"} 狀態(tài):"Refuse"

Process finished with exit code 0
  • 測試拒絕To活動中,的狀態(tài)流轉(zhuǎn)。

3.4 編寫測試類(Refuse2Revoke)

@Test
public void test_Refuse2Revoke() {
    String activityId = "100001";
    ActivityService.init(activityId, Status.Refuse);
    StateHandler stateHandler = new StateHandler();
    Result result = stateHandler.checkRevoke(activityId, Status.Refuse);
    logger.info("測試結(jié)果(拒絕To撤審):{}", JSON.toJSONString(result));
    logger.info("活動信息:{} 狀態(tài):{}", JSON.toJSONString(ActivityService.queryActivityInfo(activityId)), JSON.toJSONString(ActivityService.queryActivityInfo(activityId).getStatus()));
}

測試結(jié)果

23:59:50.197 [main] INFO  org.itstack.demo.design.test.ApiTest - 測試結(jié)果(拒絕To撤審){"code":"0000","info":"撤銷審核完成"}
23:59:50.208 [main] INFO  org.itstack.demo.design.test.ApiTest - 活動信息:{"activityId":"100001","activityName":"早起學(xué)習(xí)打卡領(lǐng)獎(jiǎng)活動","beginTime":1593694810201,"endTime":1593694810201,"status":"Editing"} 狀態(tài):"Editing"

Process finished with exit code 0
  • 測試測試結(jié)果(拒絕To撤審),的狀態(tài)流轉(zhuǎn)。

  • 綜上以上四個(gè)測試類分別模擬了不同狀態(tài)之間的有效流轉(zhuǎn)拒絕流轉(zhuǎn),不同的狀態(tài)服務(wù)處理不同的服務(wù)內(nèi)容。

七、總結(jié)

  • 從以上的兩種方式對一個(gè)需求的實(shí)現(xiàn)中可以看到,在第二種使用設(shè)計(jì)模式處理后已經(jīng)沒有了ifelse,代碼的結(jié)構(gòu)也更加清晰易于擴(kuò)展。這就是設(shè)計(jì)模式的好處,可以非常強(qiáng)大的改變原有代碼的結(jié)構(gòu),讓以后的擴(kuò)展和維護(hù)都變得容易些。
  • 在實(shí)現(xiàn)結(jié)構(gòu)的編碼方式上可以看到這不再是面向過程的編程,而是面向?qū)ο蟮慕Y(jié)構(gòu)。并且這樣的設(shè)計(jì)模式滿足了單一職責(zé)開閉原則,當(dāng)你只有滿足這樣的結(jié)構(gòu)下才會發(fā)現(xiàn)代碼的擴(kuò)展是容易的,也就是增加和修改功能不會影響整體的變化。
  • 但如果狀態(tài)和各項(xiàng)流轉(zhuǎn)較多像本文的案例中,就會產(chǎn)生較多的實(shí)現(xiàn)類。因此可能也會讓代碼的實(shí)現(xiàn)上帶來了時(shí)間成本,因?yàn)槿绻龅竭@樣的場景可以按需評估投入回報(bào)率。主要點(diǎn)在于看是否經(jīng)常修改、是否可以做成組件化、抽離業(yè)務(wù)與非業(yè)務(wù)功能。

八、推薦閱讀

  • 1. 重學(xué) Java 設(shè)計(jì)模式:實(shí)戰(zhàn)工廠方法模式「多種類型商品不同接口,統(tǒng)一發(fā)獎(jiǎng)服務(wù)搭建場景」
  • 2. 重學(xué) Java 設(shè)計(jì)模式:實(shí)戰(zhàn)原型模式「上機(jī)考試多套試,每人題目和答案亂序排列場景」
  • 3. 重學(xué) Java 設(shè)計(jì)模式:實(shí)戰(zhàn)橋接模式「多支付渠道(微信、支付寶)與多支付模式(刷臉、指紋)場景」
  • 4. 重學(xué) Java 設(shè)計(jì)模式:實(shí)戰(zhàn)組合模式「營銷差異化人群發(fā)券,決策樹引擎搭建場景」
  • 5. 重學(xué) Java 設(shè)計(jì)模式:實(shí)戰(zhàn)外觀模式「基于SpringBoot開發(fā)門面模式中間件,統(tǒng)一控制接口白名單場景」
  • 6. 重學(xué) Java 設(shè)計(jì)模式:實(shí)戰(zhàn)享元模式「基于Redis秒殺,提供活動與庫存信息查詢場景」
  • 7. 重學(xué) Java 設(shè)計(jì)模式:實(shí)戰(zhàn)備忘錄模式「模擬互聯(lián)網(wǎng)系統(tǒng)上線過程中,配置文件回滾場景」

    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多