重構複雜條件的規則設計模式 - levelup
通過編寫if else條件語句來驗證物件是軟體開發中的一項常見任務。
想象一下,開發人員收到了以下檔案驗證要求:
- 只允許txt和html副檔名。
- txt 檔案的大小不能超過 5 MB。
- html 檔案的大小不能超過 10 MB。
- 檔名不能超過 50 個字元。
以下是如何在程式碼中實現這些需求:
public class FileValidator { public bool IsValid(FileInfo file) { if (file.Extension != "txt" && file.Extension != "html") return false; else if (file.Extension == "txt" && file.Length > 5 * 1024 * 1024) return false; else if (file.Extension == "html" && file.Length > 10 * 1024 * 1024) return false; else if (file.FullName.Length > 50) return false; return true; } } |
需求第二天可能會有所變化,這是正常情況。客戶可以要求開發者為高階使用者關閉檔名長度檢查,也可以根據管理員頁面上的設定實現啟用或禁用檔案大小檢查的功能。
public class FileValidator { public bool IsValid(FileInfo file, User user, Config config) { if (file.Extension != "txt" && file.Extension != "html") return false; else if (config.CheckFileSize && file.Extension == "txt" && file.Length > 5 * 1024 * 1024) return false; else if (config.CheckFileSize && file.Extension == "html" && file.Length > 10 * 1024 * 1024) return false; else if (file.FullName.Length > 50 && user.Status != UserStatus.Premium) return false; return true; } |
此程式碼有幾個優點和缺點。讓我們分析它們,看看它是否需要重構,或者我們是否可以讓實現保持原樣。
好處:
- 實現很簡單,所以每個熟悉 if-else 語句的開發人員都可以快速讀寫這段程式碼。
缺點:
- 該實現不可擴充套件或不可維護。目前,我們只有 3 個檔案驗證規則(大小、副檔名和檔名)和 2 個影響驗證邏輯的附加條件(使用者狀態和配置)。但在實際系統中,這些數字可能會增長到數十甚至數百。
- 程式碼不可重用。方法發展得越多,在新的上下文中重用它就越困難。條件語句中的單個謂詞根本不能重用。想象一下,有人需要在應用程式的另一部分重用這個確切的謂詞:file.FullName.Length > 50。最有可能的是,這行程式碼將被複制,因為開發人員只是害怕重構此程式碼以避免迴歸錯誤。
- 該實現很難進行單元測試,因為有很多分支,所以有些很容易錯過。
- 圈複雜度會很高,因此程式碼可能無法通過客戶或專案架構師定義的質量閾值。
複雜的條件實現有許多缺點,可以通過重構設計規則來避免。
重構規則模式
規則設計模式幫助開發人員將每個業務規則封裝在一個單獨的物件中,並將業務規則的定義與其處理分離。無需修改其餘應用程式邏輯即可新增新規則。
第一步是定義IFileValidationRule介面,該介面有一個IsValid方法,它接受一個FileInfo引數並返回一個布林值。然後我們簡單地為每個檔案驗證規則實現一個單獨的類:FileExtensionValidationRule, FileSizeForExtensionValidationRule, 和MaxFileLengthValidationRule。
public interface IFileValidationRule { bool IsValid(FileInfo info); } public class FileExtensionValidationRule : IFileValidationRule { private string[] AllowedExtensions { get; set; } public FileExtensionRule(string[] extensions) => AllowedExtensions = extensions; public bool IsValid(FileInfo info) { return AllowedExtensions.Contains(info.Extension); } } public class FileSizeForExtensionValidationRule : IFileValidationRule { private string Extension { get; set; } private long Bytes { get; set; } public FileSizeForExtensionRule(string extension, long bytes) { Extension = extension; Bytes = bytes; } public bool IsValid(FileInfo info) { if (info.Extension != Extension) return true; return info.Length <= Bytes; } } public class MaxFileLengthValidationRule : IFileValidationRule { private long Length { get; set; } public MaxFileLengthRule(long length) => Length = length; public bool IsValid(FileInfo info) => info.Length <= Length; } |
然後,開發人員只需要建立一個驗證規則的列表,並通過每個規則驗證 FileInfo 物件。
public class FileValidator { public User User { get; set; } public AdminConfig AdminConfig { get; set; } public FileValidator(User user, AdminConfig config) { User = user; AdminConfig = config; } public bool IsValid(FileInfo fileInfo) { var rules = new List<IFileValidationRule> { new FileExtensionRule(new string[] { "txt", "html" }) }; if (AdminConfig.CheckFileSize) { rules.Add(new FileSizeRule("txt", 5 * 1024 * 1024)); rules.Add(new FileSizeRule("html", 10 * 1024 * 1024)); } if (User.Status != UserStatus.Premium) { rules.Add(new MaxFileLengthRule(50)); } bool isValid = rules.All(rule => rule.IsValid(fileInfo)); return isValid; } } |
建立規則的責任可以放在一個單獨的工廠類中。
規則設計模式,像其他解決方案一樣,有其優點和缺點。
優點。
- 驗證規則可以簡單地根據各種條件,如配置設定、使用者偏好、執行時間等,以任何順序動態地組成。
- 驗證規則物件可以簡單地在應用程式的不同部分重複使用。
- 新增新的驗證規則就像向集合中新增一個新的物件一樣簡單。
- 單元測試很容易,因為與單體條件語句相比,驗證規則物件更小。
- 與單片式條件運算子相比,迴圈複雜度指標會更好。
劣勢。
- 對於新手開發者來說不是一個明顯的選擇。
相關文章
- 在複雜領域中設計軟體:領域驅動設計 - levelup
- 寫一個“特殊”的查詢構造器 – (四、條件查詢:複雜條件)
- mybatis plus 使用LambdaQueryWrapper設定複雜的條件查詢MyBatisAPP
- [譯] 設計研究的 9 條規則
- 複雜的資料結構設計求解?資料結構
- 透鏡設計的六條規則(下):設計更多的禁牌
- 萬智牌設計雜談:重複利用(上)
- 萬智牌設計雜談:重複利用(下)
- 重構改善既有的程式碼設計(重構原則)
- 架構設計 | 介面冪等性原則,防重複提交Token管理架構
- 今日頭條:iOS 架構設計雜談iOS架構
- sqlserver根據條件去除重複資料SQLServer
- 使用橋接模式設計複雜的訊息系統橋接模式
- 架構設計複雜度的6個來源架構複雜度
- 設計模式 基本規範與基本原則設計模式
- 設計模式的設計原則設計模式
- 設計模式-原型模式(Prototype)【重點:淺複製與深複製】設計模式原型
- Laravel 8.55 新新增了條件驗證規則Laravel
- 開放封閉原則與規則引擎設計模式 - devgenius設計模式dev
- Apache 的架構師們遵循的 30 條設計原則Apache架構
- 雙11規則為什麼越來越複雜?
- 使用Lambdas重構工廠設計模式設計模式
- 設計模式雜談設計模式
- 【設計模式】設計原則設計模式
- 設計原則 設計模式設計模式
- 設計模式 - 設計原則設計模式
- 設計模式系列之建造者模式(Builder Pattern)——複雜物件的組裝與建立設計模式UI物件
- 如何使用建造者模式構造複雜物件?模式物件
- 得物複雜 C 端專案的重構實踐
- 領域驅動設計DDD不具備大規模落地的條件!
- 遊戲設計的11條原則遊戲設計
- 條件編譯、多檔案程式設計、結構體編譯程式設計結構體
- 《Head First 設計模式》:單件模式設計模式
- C++設計模式的原則C++設計模式
- 接地設計基本規則
- 正確使用資料架構的五條規則 - infoworld架構
- 設計模式“6”大原則!設計模式
- 重構的原則