使用Option的正確姿勢
我們會頻繁地使用Scala的Option,用以解決類似Null Object之類的問題。某種程度講,使用Option必然會減少對空指針引用判斷的丑陋代碼,結(jié)合For Comprehension,確乎是Scala編程中的一把利器。我在博客《引入Option優(yōu)雅地保證健壯性》與《并非Null Object這么簡單》中都詳細(xì)對Option的本質(zhì)與運用進(jìn)行剖析與介紹。
然而,Option雖然好,我們卻不可“貪杯”哦!
從語義上講,Option代表一種容器(Monad)非空即有的兩種狀態(tài),例如List的headOption就是對Option的合理詮釋。那么,是否只要是兩種狀態(tài)的業(yè)務(wù)場景,就可以使用Option呢?例如,將函數(shù)的參數(shù)類型定義為Option類型,用以表示用戶傳參的選擇:傳入實際值或者不傳值。這是否是得體的姿勢?
Daniel Westheide發(fā)表的博文When Option Is Not Good Enough旗幟鮮明地表達(dá)了反對意見。他給出這樣的一個案例:根據(jù)產(chǎn)品標(biāo)題與零售商信息查詢Offer:
- def searchOffers(
- productTitle: Option[String],
- retailer: Option[Retailer]
- ): Seq[Offer] = ???
作為這個函數(shù)的調(diào)用者,我們該怎么看待這兩個Option參數(shù)傳遞的業(yè)務(wù)含義?如果productTitle為None,是表示忽略productTitle的值,僅僅搜索符合retailer條件的offers;還是搜索沒有提供productTitle的Offer記錄?同樣,retailer參數(shù)也傳遞了如此模糊不清的意圖!
好的代碼尤其是接口應(yīng)該是”不言自明“清晰地傳遞開發(fā)者意圖。落到具體的業(yè)務(wù)場景,則代碼就應(yīng)該恰到好處干凈利落地表現(xiàn)其業(yè)務(wù)含義。接口體現(xiàn)準(zhǔn)確的業(yè)務(wù)通用語言(ubiquitous language),是DDD的核心價值。
如果我們?yōu)檫@兩個搜索條件定義表達(dá)業(yè)務(wù)含義的代數(shù)數(shù)據(jù)類型(algebraic data types),如下代碼所示,表意無疑要清晰許多:
- sealed trait SearchCriteriaobject SearchCriteria {
- final case object MatchAll extends SearchCriteria
- final case class Contains(s: String) extends SearchCriteria}sealed trait RetailerCriteriaobject RetailerCriteria {
- final case object AnyRetailer extends RetailerCriteria
- final case class Only(retailer: Retailer) extends RetailerCriteria}def searchOffers(
- product: SearchCriteria,
- retailer: RetailerCriteria
- ): Seq[Offer] = ???
SearchCriteria與RetailerCriteria作為兩個查詢條件,分別提供了各自的查詢語義,顯然要比過分抽象的Some與None更加清晰可讀。
引入這樣的代數(shù)數(shù)據(jù)類型不僅可以讓代碼的表意更清晰,還可更好地應(yīng)對需求的變化。對于現(xiàn)有的SearchCriteria定義而言,倘若要牽強附會,確實可以強詞奪理地說:MatchAll就是None的語義,而Contains則對應(yīng)著Some。然而,如果需求要求增加完全匹配的查詢場景,對于Option類型而言,該如何表達(dá)?回到SearchCriteria的定義,我們可以輕松地為其增加一種類型:
- object SearchCriteria {
- final case object MatchAll extends SearchCriteria
- final case class Contains(s: String) extends SearchCriteria
- final case class Exactly(s: String) extends SearchCriteria}
比較Option而言,增加了一種新的類型,卻極大地提高了代碼的可讀性,也為代碼的未來擴展奠定了基礎(chǔ)。與獲得的收益相比,僅僅是付出新增類型的微末代價,何足道哉!
【本文為51CTO專欄作者“張逸”原創(chuàng)稿件,轉(zhuǎn)載請聯(lián)系原作者】