1. 程式人生 > >設計模式之禪之混編【工廠方法模式+策略模式】

設計模式之禪之混編【工廠方法模式+策略模式】

設計模式之禪混編2

工廠方法模式+策略模式

  • 迷你版的交易系統
    • “一卡通”專案聯機交易子系統
    • 集團公司的架構
      • 總部
      • 省級分部
      • 市級機構
    • 業務要求
      • 推廣到全國,在山西能做的事在全國的其他地方也能做
      • 對於聯機子專案,異地分支機構與總部之間的通訊
    • 聯機交易系統有一個非常重要的子模組--扣款子模組
      • 從業務上說:扣款失敗就代表著所有的商業交易關閉,這是不允許發生的
      • 從技術上說:扣款子模組的異常處理、事物處理、魯棒性都是不容忽視的
  • 詳細分析一下扣款子系統
    • 每個員工都有一張IC卡,它的IC卡上有以下兩種金額
      1. 固定金額
        • 員工不可以提現的金額,只能用來特定的消費
      2. 自由金額
        • 可以提現的
  • 實際開發中
    • 架構設計採用的是一張IC卡繫結兩個賬戶:固定賬戶和自由賬戶;既然有消費,系統肯定有扣款處理,系統內有兩套扣款原則
      • 扣款策略一
        1. 兩個金額受影響:
            1. IC卡固定餘額=IC卡現有固定餘額-交易金額/2
            2. IC卡自由余額=IC卡現有自由金額-交易金額/2
        
      • 扣款策略二
        1. 全部從自由金額上扣除
        

專案的重點

  • 兩種扣款的策略怎們設計?要知道這種聯機交易,日後允許大規模變更的可能性基本是零,所以系統設計的時候要做到可拆卸,避免日後維護的大量開支
  • 很明顯,這是一個策略模式的實際應用,但是策略模式是有缺陷的,它的具體策略必須暴露出去,而且還要有上層模組初始化,這不合適,與迪米特法則有衝突,高層次模組對低層次的呼叫應該僅僅處在“接觸”的層次上,而不應該是耦合的關係,否則的話,維護的工作量會非常的大。
    • 修改缺陷--解耦:工廠模式產生物件------新的問題(要指定一個類才能產生物件)
      • 修改缺陷--引入一個配置檔案進行對映,避免系統僵化情況的發生,以列舉類完成該任務
    • 另一個問題:一個交易的扣款模式是固定的,根據其交易的編號而定,那我們怎樣把交易編號與扣款策略對應起來?
      1. 狀態模式:認為交易編號就是一個交易物件的狀態,狀態只有一個,執行完後立即結束,不存在多狀態的問題
      2. 責任鏈模式:交易編碼作為鏈中的判斷依據,由每個執行節點進行判斷,返回相應的扣款模式【簡化對應關係:使用if的判斷語句來進行簡化】
    • 這麼複雜的系統應該封裝一下,不能讓上層的業務模組直接深入到模組的內部,於是需要門面模式的封裝一下

類圖

  • IC卡類
  • 交易類
  • 扣款策略類圖
  • 策略工廠類圖
  • 扣款子模組完整類圖

具體程式碼

  • IC卡類

    package com.peng.hb2;
    
    /**
     * @author kungfu~peng
     * @data 2018年1月18日
     * @description
     */
    public class Card {
        // IC卡號
        private String cardNo = "";
        // 卡內的固定交易金額
        private int steadyMoney = 0;
        // 卡內的自由金額
        private int freeMoney = 0;
    
        // getter/setter方法
        public String getCardNo() {
            return cardNo;
        }
    
        public void setCardNo(String cardNo) {
            this.cardNo = cardNo;
        }
    
        public int getSteadyMoney() {
            return steadyMoney;
        }
    
        public void setSteadyMoney(int steadyMoney) {
            this.steadyMoney = steadyMoney;
        }
    
        public int getFreeMoney() {
            return freeMoney;
        }
    
        public void setFreeMoney(int freeMoney) {
            this.freeMoney = freeMoney;
        }
    
    }
    
  • 交易類

    package com.peng.hb2;
    
    /**
     * @author kungfu~peng
     * @data 2018年1月18日
     * @description
     */
    public class Trade {
        // 交易編號
        private String tradeNo = "";
        // 交易金額
        private int amount = 0;
    
        // getter/setter方法
        public String getTradeNo() {
            return tradeNo;
        }
    
        public void setTradeNo(String tradeNo) {
            this.tradeNo = tradeNo;
        }
    
        public int getAmount() {
            return amount;
        }
    
        public void setAmount(int amount) {
            this.amount = amount;
        }
    
    }
    
  • 扣款策略介面

    package com.peng.hb2;
    
    /**
     * @author kungfu~peng
     * @data 2018年1月18日
     * @description
     */
    public interface IDeduction {
        // 扣款,提供交易和卡資訊,進行扣款,並返回扣款是否成功
        public boolean exec(Card card, Trade trade);
    }
    
  • 扣款策略一

    package com.peng.hb2;
    
    /**
     * @author kungfu~peng
     * @data 2018年1月18日
     * @description
     */
    public class SteadyDeduction implements IDeduction {
    
        // 固定性交易扣款
        @Override
        public boolean exec(Card card, Trade trade) {
            // 固定金額和自由金額各扣除50%
            int halfMoney = (int) Math.rint(trade.getAmount() / 2.0);
            card.setFreeMoney(card.getFreeMoney() - halfMoney);
            card.setSteadyMoney(card.getSteadyMoney() - halfMoney);
            return true;
        }
    
    }
    
  • 扣款策略二

    package com.peng.hb2;
    
    /**
     * @author kungfu~peng
     * @data 2018年1月18日
     * @description
     */
    public class FreeDeduction implements IDeduction {
        // 自由扣款
        @Override
        public boolean exec(Card card, Trade trade) {
            // 直接從自由余額中扣除
            card.setFreeMoney(card.getFreeMoney() - trade.getAmount());
            return true;
        }
    
    }
    
  • 扣款策略的封裝

    package com.peng.hb2;
    
    /**
     * @author kungfu~peng
     * @data 2018年1月18日
     * @description
     */
    public class DeductionContext {
        // 扣款策略
        private IDeduction deduction = null;
    
        // 建構函式傳遞策略
    
        public DeductionContext(IDeduction deduction) {
            super();
            this.deduction = deduction;
        }
    
        // 進行扣款
        public boolean exec(Card card, Trade trade) {
            return this.deduction.exec(card, trade);
        }
    }
    
  • 策略列舉

    package com.peng.hb2;
    
    /**
     * @author kungfu~peng
     * @data 2018年1月18日
     * @description 策略列舉
     */
    public enum StrategyMan {
        SteadyDeduction("com.peng.hb2.SteadyDeduction"), FreeDeduction(
                "com.peng.hb2.FreeDeduction");
    
        String value = "";
    
        private StrategyMan(String _value) {
            this.value = _value;
        }
    
        public String getValue() {
            return value;
        }
    
    }
    
  • 策略工廠

    package com.peng.hb2;
    
    /**
     * @author kungfu~peng
     * @data 2018年1月18日
     * @description
     */
    public class StrategyFactory {
        // 策略工廠
        public static IDeduction getDeduction(StrategyMan strategy) {
            IDeduction deduction = null;
            try {
                deduction = (IDeduction) Class.forName(strategy.getValue())
                        .newInstance();
            } catch (Exception e) {
                System.err.println(e.getMessage());
            }
            return deduction;
        }
    }
    
  • 扣款模組的封裝

    package com.peng.hb2;
    
    /**
     * @author kungfu~peng
     * @data 2018年1月18日
     * @description 扣款模組封裝
     */
    public class DeductionFacade {
        // 對外公佈的扣款資訊
        public static Card deduct(Card card, Trade trade) {
            // 獲得消費策略
            StrategyMan reg = gerDeductionType(trade);
            // 初始化一個消費策略物件
            IDeduction deduction = StrategyFactory.getDeduction(reg);
            // 產生一個策略的上下文
            DeductionContext context = new DeductionContext(deduction);
            // 進行扣款處理
            context.exec(card, trade);
            // 返回扣款處理完畢的資料
            return card;
        }
    
        // 獲得對應商戶的消費策略
        private static StrategyMan gerDeductionType(Trade trade) {
            // 模擬操作
            if (trade.getTradeNo().contains("peng")) {
                return StrategyMan.FreeDeduction;
            }else{
                return StrategyMan.SteadyDeduction;
            }
        }
    }
    
  • 場景類

    package com.peng.hb2;
    
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    
    /**
     * @author kungfu~peng
     * @data 2018年1月18日
     * @description
     */
    public class Client {
        // 模擬交易
        public static void main(String[] args) {
            // 初始化一張IC卡
            Card card = initIC();
            // 顯示一下卡的資訊
            System.out.println("=======初始化資訊========");
            showCard(card);
            // 是否停止執行的標誌
            boolean flag = true;
            while (flag) {
                Trade trade = createTrade();
                DeductionFacade.deduct(card, trade);
                // 交易成功,打印出成功處理資訊
                System.out.println("\n========交易憑證===========");
                System.out.println(trade.getTradeNo() + "交易成功!");
                System.out.println("本次發生的交易金額為:" + trade.getAmount() * 100 / 100.0
                        + "元");
                // 展示一下卡內的資訊
                showCard(card);
                System.out.println("是否需要退出?(Y/N)");
                if (getInput().equalsIgnoreCase("y")) {
                    flag = false;
                }
            }
    
        }
    
        /**
         * 獲取輸入
         * 
         * @throws IOException
         */
        private static String getInput() {
            String str = "";
            try {
                str = (new BufferedReader(new InputStreamReader(System.in)))
                        .readLine();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return str;
        }
    
        /**
         * 產生一個交易
         */
        private static Trade createTrade() {
            Trade trade = new Trade();
            System.out.println("請輸入交易編碼:");
            trade.setTradeNo(getInput());
            System.err.println("請輸入交易金額:");
            trade.setAmount(Integer.parseInt(getInput()));
            // 返回交易
            return trade;
        }
    
        /**
         * 功能:列印Card的資訊
         */
        private static void showCard(Card card) {
            System.out.println("IC卡編號:" + card.getCardNo());
            System.out.println("固定型別餘額:" + card.getSteadyMoney() * 100 / 100.0
                    + "元");
            System.out.println("自由型別餘額:" + card.getFreeMoney() * 100 / 100.0 + "元");
        }
    
        /**
         * 功能:初始化一個Card
         */
        private static Card initIC() {
            Card card = new Card();
            card.setCardNo("1104561811");
            card.setFreeMoney(100000);
            card.setSteadyMoney(800000);
            return card;
        }
    }
    
  • 執行結果

    =======初始化資訊========
    IC卡編號:1104561811
    固定型別餘額:800000.0元
    自由型別餘額:100000.0元
    請輸入交易編碼:
    666
    請輸入交易金額:
    123
    
    ========交易憑證===========
    666交易成功!
    本次發生的交易金額為:123.0元
    IC卡編號:1104561811
    固定型別餘額:800000.0元
    自由型別餘額:99877.0元
    是否需要退出?(Y/N)
    n
    請輸入交易編碼:
    888
    請輸入交易金額:
    345
    
    ========交易憑證===========
    888交易成功!
    本次發生的交易金額為:345.0元
    IC卡編號:1104561811
    固定型別餘額:800000.0元
    自由型別餘額:99532.0元
    是否需要退出?(Y/N)
    y
    

混編模式小結

  • 回顧一下扣款子模組
    • 策略模式
      • 負責對扣款策略進行封裝,保證兩個策略可以自由切換,而且日後增加扣款策略也非常簡單容易
    • 工廠方法模式
      • 修正策略模式必須對外暴露具體策略的問題,由工廠方法模式直接產生一個具體的策略物件,而其他模組則不需要依賴具體的策略
    • 門面模式
      • 負責對複雜的扣款系統進行封裝,封裝的結果就是避免高層模組深入子系統內部,同事提供系統的高內聚、低耦合的特性
  • 扣款策略變更
    • 增加一個新的扣款策略,三步就可以完成:
      1. 實現IDeduction介面
      2. 增加StrategyMan配置項
      3. 擴充套件扣款策略的利用--也就是門面模式的getDeductionType方法,在實際專案中,這裡只需增加資料庫的配置項
    • 減少一個策略
      • 只需修改扣款的利用
    • 變更一個策略
      • 擴充套件一個實現類口就可以
  • 變更扣款策略的利用規則
    • 如果系統不想大改,還記得狀態模式嗎?這個就是為策略的利用服務的,變更它就能滿足要求,想把IC卡也納入策略利用的規則也不復雜。其實這個變更還真發生了,系統投產後,業務提出考慮退休人員的情況,退休人員的IC卡與普通在職員工一樣,但是它的扣款不僅僅是根據交易編碼,還要根據IC卡物件,系統的變更做法是增加一個扣款策略,同時擴充套件扣款利用策略,也就是資料庫的配置項,在getDeductionType中擴充套件一個功能:根據IC卡號,確認是否是退休人員,是退休人員,則使用新的扣款策略,這是一個非常簡單的擴充套件
  • 交融交易系統沒什麼複雜的,剩下的問題就是開始考慮系統的魯棒性--這,才是難點

相關推薦

設計模式工廠方法模式+策略模式

設計模式之禪混編2工廠方法模式+策略模式迷你版的交易系統“一卡通”專案聯機交易子系統集團公司的架構總部省級分部市級機構業務要求推廣到全國,在山西能做的事在全國的其他地方也能做對於聯機子專案,異地分支機構

設計模式命令模式+責任鏈模式

設計模式之禪之混編 命令模式+責任鏈模式 搬移UNIX命令 在Windows系統上使用UNIX命令 UNIX命令 命令名 選項 運算元 UNIX規定 命令名為小寫字母 命令名、選項、操作樹

設計模式建立類PK工廠模式VS建造者模式

設計模式之禪PK之建立類 建立類設計模式 建立類模式: 工廠方法模式 建造者模式 抽象工廠模式 單例模式 原型模式 建立者模式的功能: 提供物件的建立和管理職責 工廠方法模式、抽象工廠方法模

手工脫殼 未知IAT加密殼 IAT加密+混淆+花指令哈希加密OD腳本

get 並且 size pos targe 特殊 pro 臨界點 post 一、工具及殼介紹 使用工具:Ollydbg,PEID,ImportREC,LoadPE,OllySubScript 未知IAT加密殼: 二、初步脫殼 嘗試用E

Mac系統 + Python + Django開發一個釋出會系統Django模型(二) Mac系統 + Mysql安裝Mysql資料庫 Python + Mysql用pymysql庫連線Mysql資料庫並進行增刪改查操作

上一部分給大家介紹Django的檢視。 接下來繼續來了解Django框架,來看第二部分,此部分是對資料庫的操作。   目錄: 一、設計系統表 二、admin後臺管理 三、基本資料訪問(SQLite資料庫) 四、Django配置MySQL   &

Java8 程式設計規範入門forEach方法遍歷集合

在Java中我們需要處理Clloection的時候,通常需要建立一個Iterator例項來對集合進行迭代,在迭代中對每個或者某些元素進行業務邏輯的操作。如果迭代使用不當的話,則會丟擲ConcurrentModificationException異常。 舊API、新的forEach API進行遍歷列印集合中的

JAVA設計模式(24):建立型-工廠模式工廠方法模式(Factory Method)

簡單工廠模式雖然簡單,但存在一個很嚴重的問題。當系統中需要引入新產品時,由於靜態工廠方法通過所傳入引數的不同來建立不同的產品,這必定要修改工廠類的原始碼,將違背“開閉原則”,如何實現增加新產品而不影響已有程式碼?工廠方法模式應運而生,本文將介紹第二種工廠模式——工廠方法模

工廠方法模式-Factory Method Pattern 工廠三兄弟工廠方法模式(四):過載的工廠方法工廠方法的隱藏,工廠方法模式總結

5 過載的工廠方法        Sunny公司開發人員通過進一步分析,發現可以通過多種方式來初始化日誌記錄器,例如可以為各種日誌記錄器提供預設實現;還可以為資料庫日誌記錄器提供資料庫連線字串,為檔

解決CentOS 6.5 安裝沒有網路的問題 轉自雲棲社群

摘要: 本文講的是解決CentOS 6.5 安裝之沒有網路的問題, 昨天下了個CentOS 6.5 Minimal 版,在VMware 10下安裝好了之後,發現上不了網,PING外網也PING不通。 在網上搜了一下,發現Linux安裝好了之後,網絡卡預設是沒有啟動的,下面

設計模式簡單介紹(Java語言)-- 工廠方法模式

設計模式簡單介紹(Java語言) – 工廠方法模式 1.工廠方法模式的定義: 工廠方法模式(FACTORY METHOD)是一種常用的物件建立型設計模式,此模式的核心精神是封裝類中不變的部分,提取其中個性化善變的部分為獨立類,通過依賴注入以達到解耦

二、使用簡單工廠來改進策略模式

麻煩 str 不同 [] ring www ride say 模式 策略模式的使用,把一系列算法進行了封裝,只需要通過配置不同的算法,即可以實現算法的自由切換。具體內容參考第一篇:http://www.cnblogs.com/lay2017/p/7570041.html 但

對 橋接模式 的個人理解,以及與 工廠方法模式、建造者模式 的結合運用

學習了一段時間設計模式,就想分享一下自己的理解, 歡迎大家多多指點,指出不足之處哈 橋接模式:以商店與手機為例子來描述,先從簡單的依賴關係說起 public interface Phone { /** 充電 **/ void charge(); /** 解鎖 *

正確方法,流程註解清晰MySQL自定義函式生成隨機身份證號碼

有需要用MySQL生成隨機身份證號碼,沒有在網上搜到,於是自己寫了下。年前寫了一部分,年後營養豐富後在曠工一天後活力滿滿,一鼓作氣的寫成了。其中完全按照【身份證演算法】實現,也將實現步驟拆成了獨立的函式,清晰的解釋了方法功能,對步驟進行了一定講解,但是還有可以優化的地方,這就

Effective Java學習筆記一(靜態工廠方法、JavaBeans模式、builder模式

靜態工廠方法代替構造器 對於類而言,為了讓客戶端獲取它自身的一個例項,最常用的辦法是提供一個公有的構造器。還有一種方法:公有的靜態工廠方法。它只是一個返回類例項的靜態方法,如下程式碼所示: public class Gender { pri

C#設計模式十三模板方法模式(Template Method Pattern)行為型

並集 client 變化 args 集中 pac 爸爸 rim 自己 原文:C#設計模式之十三模板方法模式(Template Method Pattern)【行為型】一、引言 “結構型”的設計模式已經寫完了,從今天我們開始講“行為型”設計模式。現在我們開始講【行為型】設

C#設計模式十九策略模式(Stragety Pattern)行為型

多條件 第一次 必須 this 數據傳遞 名稱 面向 div 想要 原文:C#設計模式之十九策略模式(Stragety Pattern)【行為型】一、引言 今天我們開始講“行為型”設計模式的第七個模式,該模式是【策略模式】,英文名稱是:Stragety Pattern。

設計模式學習筆記 策略模式

實際類型 clas equals 的確 ron 設計 poj 擴展 打印sql 簡介: 經常網購的可能發現京東、淘寶等電商平臺每到什麽節日都會進行打折,這種打折就是一種策略,策略模式的意思呢,就是把不變的和易變的策略分離開,需要什麽策略時候,把需要的策略傳給執行體,而不是執

javascriptjavasrcipt設計模式策略模式

影響 配置 可能 pan style 類庫 ava 自由 AC 策略模式支持在運行時由使用者選擇合適的算法,對於使用者而言不用關心背後的具體事項,而使用者自動根據當前程序執行的上下文和配置,從已有的算法列表中選擇出合適的算法來處理當前任務。 1.要解決的問題 2.如何實現

設計模式(1)-設計準則

精簡 是個 依賴關系 項目 開閉 結果 bubuko 自己的 HR   最近幾周一直都在看設計模式之禪,看的過程當中,發現大多數的設計模式在平時編碼過程當中使用到了,當時沒意識到這就是設計模式的一種,翻看自己以前的代碼,有些設計顯然和設計模式的標準有出入,但是個人認為設計模

javaScript設計模式面向對象程(object-oriented programming,OOP)(一)

email 全局變量 color javascrip 原型 obj 只有一個 沒有 ted 面試的時候,總會被問到,你對javascript面向對象的理解? 面向對象編程(object-oriented programming,OOP)是一種程序設計範型。它講對象