重構複雜條件的規則設計模式 - 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; } } |
建立規則的責任可以放在一個單獨的工廠類中。
規則設計模式,像其他解決方案一樣,有其優點和缺點。
優點。
- 驗證規則可以簡單地根據各種條件,如配置設定、使用者偏好、執行時間等,以任何順序動態地組成。
- 驗證規則物件可以簡單地在應用程式的不同部分重複使用。
- 新增新的驗證規則就像向集合中新增一個新的物件一樣簡單。
- 單元測試很容易,因為與單體條件語句相比,驗證規則物件更小。
- 與單片式條件運算子相比,迴圈複雜度指標會更好。
劣勢。
- 對於新手開發者來說不是一個明顯的選擇。
相關文章
- 重構遺留程式碼(3):複雜的條件語句
- 複合條件查詢的重構
- 在複雜領域中設計軟體:領域驅動設計 - levelup
- 寫一個“特殊”的查詢構造器 – (四、條件查詢:複雜條件)
- [譯] 設計研究的 9 條規則
- mybatis plus 使用LambdaQueryWrapper設定複雜的條件查詢MyBatisAPP
- 複雜的資料結構設計求解?資料結構
- 架構之重構的12條軍規架構
- 重構改善既有的程式碼設計(重構原則)
- 今日頭條:iOS 架構設計雜談iOS架構
- [譯] 為複雜產品制定設計規範
- 架構設計 | 介面冪等性原則,防重複提交Token管理架構
- JavaScript 設計模式 :用組合模式寫出複雜元件JavaScript設計模式元件
- 架構設計複雜度的6個來源架構複雜度
- 使用橋接模式設計複雜的訊息系統橋接模式
- 設計模式 基本規範與基本原則設計模式
- Laravel 8.55 新新增了條件驗證規則Laravel
- 設計模式-原型模式(Prototype)【重點:淺複製與深複製】設計模式原型
- 設計模式的設計原則設計模式
- 所有程式設計師都應該遵守的 11 條規則程式設計師
- 開放封閉原則與規則引擎設計模式 - devgenius設計模式dev
- 使用Lambdas重構工廠設計模式設計模式
- 設計模式雜談設計模式
- 軟體設計的複雜度複雜度
- 重新封裝一個iptables防止規則重複封裝
- Apache 的架構師們遵循的 30 條設計原則Apache架構
- 設計原則 設計模式設計模式
- 設計模式 - 設計原則設計模式
- 【設計模式】設計原則設計模式
- 如何使用建造者模式構造複雜物件?模式物件
- 設計模式系列之建造者模式(Builder Pattern)——複雜物件的組裝與建立設計模式UI物件
- 程式設計規則程式設計
- 遊戲設計的11條原則遊戲設計
- 複雜 Activity UI 介面模組化重構實踐UI
- 得物複雜 C 端專案的重構實踐
- 重構遺留程式碼(6):進攻複雜的方法
- 手遊開發者談遊戲營收模式設計中的最重要規則遊戲營收模式
- make的模式規則模式