两种参数类型_布尔参数这些缺点不能忍?不如试试枚举吧
在代碼庫中使用布爾標志值來管理狀態機似乎聽起來是個不錯的辦法,但事實并非如此。布爾值恐怕是很多程序員接觸到的第一種數據類型,它非常簡單,只有兩種狀態: true 和false.
隨著代碼的發展,它很容易產生代碼復雜性、可讀性和可伸縮性等方面的問題。
通常,標記參數會劃分函數的邏輯,迫使該函數根據值執行多項操作,這可能會導致業務邏輯中的混亂運行,代碼庫很容易會以下列樹狀結構結束運行:
背景故事
下面這個故事把布爾參數在狀態機和函數參數中的弱點體現得淋漓盡致。
一群軟件開發人員曾經建立了一個管理用戶狀態的模塊,其中一名開發者堅持使用布爾值,因為該模塊要求只有兩個狀態:ONLINE 和 OFFLINE,看起來又快又簡單,直截了當。盡管大多數人并不完全同意該建議,但還是繼續做了下去。
最終,類似于下面這樣的函數開始占據代碼庫:
func setUserState(isUserOnline :Bool)不久后,團隊里來了一位新成員,他想知道下面語句的真正含義:
setUserState(true) //The new guyjust kept staring at this.雖然有人提出了一個看起來更好的函數名(setUserOnline),但當一旦出現新的業務需求(包括另一個用戶狀態:BLOCKED),事情就變成了一場噩夢。來看看這些開發人員都采取了哪些辦法來解決這個問題。
三態布爾問題
布爾值通常表示兩種狀態,但在某些語言中(如Java使用的是Boolean對象),可以用null來分配第三種狀態。在上下文中,BLOCKED 會被設置為null。
圖源:unsplash雖然這似乎不需要額外布爾值就可以適應新的用戶狀態,但會很容易得到NullPointerExceptions結果。
另外,在不同情況下,要想區分false和null會比較棘手。比如,布爾屬性game.isPlaying為true時則清楚地表明游戲處于播放模式。但為false或null時,false表示游戲暫停或停止。
如你所見,false沒有提供足夠信息來輕松識別和回憶其綁定狀態,三態布爾值只會使邏輯復雜化。
此外,當系統要求包含另一個稱為EXPIRED的狀態,會出現什么情況?由于現在有四個狀態,這種方法無法解決了。接著來看看大多數開發人員采用的另一種方法。
多個布爾值帶來隱藏的依賴項
開發人員最終擴展了前一個函數的簽名,為新狀態添加了兩個布爾參數:
func setUserState( isUserOnline : Bool, isUserBlocked : Bool, isUserExpired : Bool)看起來像是滿足業務需求的簡單擴展,卻被迫在代碼庫中引入了隱藏依賴項和大量新組合。
創建的兩個隱藏依賴項是isUserOnline — isUserExpired和 isUserOnline — isUserBlocked。現在需要明確管理額外狀態,以避免沖突狀態。例如,被攔截/過期的用戶不能在線。以下是要處理的兩種沖突狀態的示例:
#Condition 1: isUserOnline:false and isUserExpired: true #Condition 2: isUserOnline: false and isUserBlocked: true添加狀態越多,函數很容易就能變成一長串參數。事情變得不可持續,因為最終會遇到很多&&,||,和其他復雜的分支邏輯來處理互斥和相關的布爾值。
布爾值具有類型安全性和可讀性問題
使用多個布爾值,很有可能將它們混淆,最終還可能會傳遞錯誤值(可能來自其他對象),且編譯器甚至不會作出反應。在重構和執行代碼審查時,這可能是一場噩夢,需要編寫大量的單元測試來解決此類問題。
圖源:unsplash此外,很容易忘記布爾變量的false或true值的真正含義,理解充滿布爾值的函數調用(如下所示)只會變得非常困難:
setUserState(true, false, false)有人可能會說,現在很多編程語言都支持可以提高函數可讀性的命名參數。但話又說回來,可能會意外傳遞一個反向或不正確的布爾值,但函數簽名仍然匹配。
如果用枚舉而不是布爾值,這個故事中的軟件開發人員就能避免這些麻煩。
選用枚舉,避免用布爾值
枚舉數是一種數據類型,由一組能夠以類型安全方式使用的命名值組成。雖然它看起來可能不像布爾值那么簡單,但用枚舉或其他用戶定義類型有助于避免設置具有多個分支的復雜if語句。
enum UserStates{case active case inactive case blocked case expired}1. 枚舉清晰明了
枚舉強制命名所有狀態,這讓人很容易理解它們的含義——從而創建自文檔化代碼。同樣,枚舉清楚地表明這些值互相排斥,從而消除沖突狀態的疑問。
將枚舉作為函數中的參數傳遞更加清晰,有助于避免神秘的布爾值。只需比較下面兩行:
setUserState(true,false, false)//The version below is more concise andclearer.setUserState(UserStates.active)2. 枚舉具有類型安全性
對于枚舉,不能給它分配除指定值以外的任何值,因為其具有類型安全性,這樣就不可能出現意外交換值或傳遞無效狀態,因為能夠被編譯器發現。
并非所有語言都支持本機枚舉,在此種情況下,可以創建自定義類型。例如,在JavaScript中,可以通過“凍結”對象中的常量來解決此問題:
constUserState= {ACTIVE: 1,INACTIVE: 2,BLOCKED: 3,EXPIRED: 4};Object.freeze(UserState);3.枚舉使擴展和重構更加容易
在枚舉數中擴展值的集合更容易,因為與布爾值不同,可能狀態組合的數量不會在每種新情況下加倍。
而且,許多編譯器很智能,足以指示需要進行哪些更改才能適應新枚舉。比如,Swift會引發錯誤,同時,使用其他語言,可以輕松查明枚舉中出現的所有案例。
用額外的新案例擴展已經存在的枚舉不費吹灰之力,因為數據類型保持不變,這使得重構整體變得更加容易。
圖源:unsplash當然,布爾值也不是一無是處。如果確定狀態是二進制,且互斥,或方法名稱已經描述了狀態(例如setEnabled(true)),那么就放心大膽地用布爾值吧。
但通常情況下,需求會發生變化,也需要添加新狀態。因此包含兩個元素的枚舉值得一試,它比布爾標志更安全。枚舉有助于將來驗證代碼,且無需跟蹤布爾字段。
別貪圖省事兒濫用布爾值,枚舉是一個不錯的選擇。
留言點贊關注
我們一起分享AI學習與發展的干貨
編譯組:齊鑫濛、劉露敏
相關鏈接:https://medium.com/better-programming/dont-use-boolean-arguments-use-enums-c7cd7ab1876a
如轉載,請私信小芯,遵守轉載規范
總結
以上是生活随笔為你收集整理的两种参数类型_布尔参数这些缺点不能忍?不如试试枚举吧的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux远程windows执行cmd,
- 下一篇: Mysql无法创建外键的原因汇总_查看M