反饋法學習設計模式(一)——策略模式Strategy Pattern

来源:http://www.cnblogs.com/chenjunping/archive/2017/11/05/7788629.html
-Advertisement-
Play Games

簡介(Introduction) <! 出這邊文章的目的,及如何更好地閱讀這篇文章 之前學習 "Java8實戰" 時,遇到一個很好的策略模式示例。便想著藉著這個示例結合反饋式的方法來,學習策略設計模式,也以便後面反覆琢磨學習。 首先我們通過練習,逐步寫出符合相應需求的代碼,再根據需求進行改進、比較、 ...


簡介(Introduction)

之前學習Java8實戰時,遇到一個很好的策略模式示例。便想著藉著這個示例結合反饋式的方法來,學習策略設計模式,也以便後面反覆琢磨學習。
首先我們通過練習,逐步寫出符合相應需求的代碼,再根據需求進行改進、比較、重寫,最終得出一種更靈活的最佳實現。

練習

    /** 該類為蘋果 */
    class Apple {
        private Float weight;
        private String color;
    }
    /** 該類為蘋果過濾器 */
    public class AppleFilter {
        private Set<Apple> apples;
    }
  • 需求一,添加方法使得可以篩選綠蘋果
  • 需求二,能夠選取各種顏色的蘋果
  • 需求三,能夠篩選各種顏色, 各種重量的蘋果
  • 需求四,將篩選條件進行抽象,能篩選各種屬性
  • 需求五,使用匿名類進行改進

策略模式(Strategy Pattern)

對於策略模式,我的理解是行為參數化。行為是指處理頻繁變化需求的那段代碼。每當需求變化時,就傳遞不同的行為作為參數進行處理。如此,便是將代碼塊進行封裝,得到可進行應對變化的策略一般。

策略模式,它定義了演算法家族。分別封裝起來,讓它們之間可以相互替換,此模式讓演算法的變換,不會影響到使用演算法的客戶端。——《設計模式:可復用面向對象軟體的基礎》

  • 解決什麼矛盾:不同時間應用不同的業務規則;多重條件判斷、硬編碼所帶來的複雜及難以維護
  • 如何用代碼實現:每個策略,實現約定的介面及方法。
  • 優點:耦合性低(降低各種策略類與調用者的耦合)、擴展性強、代碼簡潔(策略封裝了變化的條件、避免了多重判斷)
  • 缺點:策略類膨脹、代碼繁瑣

UML

策略模式.PNG

代碼實現

package Demo.filter;

//
// 該類用於篩選蘋果
// 代碼質量要求:更加抽象通用, 更加簡潔
// 以下七次的代碼修改也相應反映代碼的質量及水平
//
// Created by auhnayuil on 17-9-24.
//
public class FilterApple implements Filter<Apple> {

    //
    // 第一次需求:選取綠色蘋果
    // 該方法純粹為篩選出綠色蘋果
    // 篩選蘋果的條件為常量, 很難適應客戶或者調用者的需求變化
    //
    public Set<Apple> filterGreenApple(Set<Apple> apples){
        Set<Apple> result = new HashSet<>();
        for(Apple apple : apples){
            if("green".equals(apple.getColor()))
                result.add(apple);
        }
        return result;
    }

    //
    // 第二次需求變化:能夠選取各種顏色的蘋果
    // 將顏色提取為方法的參數, 更靈活地適應篩選各種顏色的蘋果
    //
    // 一個良好的原則是在編寫某個需求多變的代碼時, 嘗試將其抽象化
    ///
    public Set<Apple> filterAppleByColor(Set<Apple> apples, String color){
        Set<Apple> result = new HashSet<>();
        for(Apple apple : apples){
            if(apple.getColor().equals(color))
                result.add(apple);
        }
        return result;
    }

    //
    // 第三次需求變化:能夠篩選各種顏色, 各種重量的蘋果
    // 需求變化的因素除了單一元素上變化, 還表現為多元素上變化
    //
    // 一旦多屬性被要求組合查詢, 進行更複雜的查詢時
    // 篩選條件及使用上將會變得非常笨拙及醜陋
    ///
    public Set<Apple> filterApples(Set<Apple> apples, String color, Float weight){
        Set<Apple> result = new HashSet<>();
        for(Apple apple : apples){
            if(     apple.getColor().equals(color)
                    && apple.getWeight() > weight)
                result.add(apple);
        }
        return result;
    }

    //
    // 第四次嘗試:將篩選條件進行抽象, 將行為參數化
    // 更高層次的抽象為將選擇條件進行建模, 即形成一種可進行選擇的通用的策略
    //
    // 模型描述:根據對象的某些屬性來返回一個布爾值
    // 類似於"謂詞"這樣的語義
    //
    // 至於為何要在方法參數中抽象篩選條件為一個介面?
    //
    // 到這裡, filterApples的行為僅取決於 Predicate對象所傳遞的代碼, 也就是
    // 所謂的 向一個參數傳遞了代碼, 或者行為參數化了
    //
    // 值需要創建包裹著不同篩選條件的代碼塊 的Predicate對象就可以實現不同的行為了
    ///
    public List<Apple> filterApples(List<Apple> apples, Predicate<Apple> predicate){
        return (List<Apple>) collect(apples, predicate);
    }

    //
    // 第五嘗試:匿名類
    // 沒有變數名, 允許你同時聲明並實例化一個類
    //
    ///
    public Set<Apple> filterApplesByAnonymousClass(Set<Apple> apples){
        return (Set<Apple>) collect(apples, new Predicate<Apple>() {
            @Override
            public boolean test(Apple target) {
                return ("red".equals(target.getColor()) && target.getWeight() > 0.0F);
            }
        });
    }

    //
    //  第六次嘗試:Lambda表達式 以及 抽象結果集
    //  可以改寫為以下形式:
    //  filterApplesByLambda(apples, (Apple apple) -> "red".equals(apple.getColor()));
    //
    //  那麼如何用Lambda改寫一個內部類?
    //
    public Set<Apple> filterApplesByLambda(Collection<Apple> apples, boolean is){
        Set<Apple> result = new HashSet<>();
        for(Apple apple : apples){
            if(is)
                result.add(apple);
        }
        return result;
    }
}
package Demo.filter;

//
// 該方法為最基本的過濾器
// 用於抽象各個過濾器中的迴圈, 遍歷, 收集等重覆行為
// 採用介面的預設方法實現
//
// Created by auhnayuil on 17-9-24.
//
public interface Filter<T> {

    default Collection<T> collect(Collection<T> targets, Predicate<T> predicate) {
        Class<? extends Collection> clazz = targets.getClass();
        Collection result = null;
        try {
             //該部分代碼塊, 通過反射生成集合的實例對象. 得到一個空的結果集對象
             result = clazz.newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        for(T target : targets){
            //迴圈遍歷目標集合, 並且通過介面形成策略判斷是否符合過濾器條件
            //收集符合條件的結果
            if(predicate.test(target))
                result.add(target);
        }
        return result;
    }
}
package Demo.predicate;

//
// 策略設計模式(Staregy)
// 定義了一系列的演算法族, 並將其封裝, 可以相互替換且在運行時選擇所需要的合適的"策略"
// Created by auhnayuil on 17-9-24.
//
public class AppleRedAndWeightPrdicate implements Predicate<Apple> {

    @Override
    public boolean test(Apple target) {
        return ("red".equals(target.getColor())
                && target.getWeight() > 0.0F);
    }
}

參考鏈接

[Java8實戰] https://book.douban.com/subject/26772632/
[Baidu] https://baike.baidu.com/item/%E7%AD%96%E7%95%A5%E6%A8%A1%E5%BC%8F/646307?fr=aladdin
[菜鳥教程] http://www.runoob.com/design-pattern/strategy-pattern.html


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

-Advertisement-
Play Games
更多相關文章
  • 前言 轉載請標明出處:http://www.cnblogs.com/smfx1314/p/7795837.html 本項目是我寫的一個練習,目的是回顧ssh框架的整合以及使用。項目介紹:此項目主要有前臺管理員通過登錄進入員工管理系統頁面,之後可以對員工列表進行常規的增刪改查。以及部門列表的增刪改查。 ...
  • 1 /*造人*/ 2 public class Tman { 3 public int id; 4 public String name; 5 public int age; 6 public String city; 7 public String introduce() { 8 return + ...
  • if語句應用之——求最大值 while迴圈 例題1:輸出1到100之間的偶數-(不過我覺得我的程式好蠢) 簡化的: 註意: while 後的條件判斷語句,一定要在程式中做flase打斷,不然會無休止的運行下去 ...
  • demo需求: 用java第三方調用郵箱(主要是qq郵箱)發送郵件給一方或多方 demo主要技術: 1.引入一個額外的jar包,javax.mail.jar,開啟java操作郵箱的功能 2.Properties類的使用 demo主要代碼展示: Java / show 方法簡介 第三方調用郵箱(這裡主 ...
  • name=[ 'coco', 'liux', 'zhangyq', 'liufupengdozhao']# 使用rang()方法for i in range(0,len(name)): print(i,name[i])# 上面的方法有些累贅,使用內置enumerrate函數會有更加直接for i i... ...
  • 上一篇文章直接就被移除首頁了,這次來點大家都能懂的乾貨. 需求 之前做一個winform的工具時候有以下幾個需求1. 主窗體(或者叫平臺)可以安裝若幹類型的插件。2. 插件關閉時候需要保存狀態。3. 插件載入的時候可以載入上次關閉的配置。4. 插件中的配置可以切換。5. 主窗體本身保存當前插件,並且 ...
  • Hitchhiker是一個在github上開源的項目,被善友大哥收錄到了它的微服務工具包里《開源的 Restful Api 集成測試工具 Hitchhiker》,同時源代碼也開源到了github上https://github.com/brookshi/Hitchhiker 多樣化的部署 deploy ...
  • 工廠模式(Factory Pattern) 1,Shape介面的定義 2,Circle實現Shape介面 3,Rectangle實現Shape介面 4,Square實現Shape介面 5,工廠方法類ShapeFactory 6,工廠的使用Demo ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...