使用Option的正确姿势

开发 开发工具
我们会频繁地使用Scala的Option,用以解决类似Null Object之类的问题。某种程度讲,使用Option必然会减少对空指针引用判断的丑陋代码,结合For Comprehension,确乎是Scala编程中的一把利器。

[[184061]]

我们会频繁地使用Scala的Option,用以解决类似Null Object之类的问题。某种程度讲,使用Option必然会减少对空指针引用判断的丑陋代码,结合For Comprehension,确乎是Scala编程中的一把利器。我在博客《引入Option优雅地保证健壮性》与《并非Null Object这么简单》中都详细对Option的本质与运用进行剖析与介绍。

然而,Option虽然好,我们却不可“贪杯”哦!

从语义上讲,Option代表一种容器(Monad)非空即有的两种状态,例如List的headOption就是对Option的合理诠释。那么,是否只要是两种状态的业务场景,就可以使用Option呢?例如,将函数的参数类型定义为Option类型,用以表示用户传参的选择:传入实际值或者不传值。这是否是得体的姿势?

Daniel Westheide发表的博文When Option Is Not Good Enough旗帜鲜明地表达了反对意见。他给出这样的一个案例:根据产品标题与零售商信息查询Offer:

  1. def searchOffers( 
  2. productTitle: Option[String], 
  3. retailer: Option[Retailer] 
  4. ): Seq[Offer] = ??? 

作为这个函数的调用者,我们该怎么看待这两个Option参数传递的业务含义?如果productTitle为None,是表示忽略productTitle的值,仅仅搜索符合retailer条件的offers;还是搜索没有提供productTitle的Offer记录?同样,retailer参数也传递了如此模糊不清的意图!

好的代码尤其是接口应该是”不言自明“清晰地传递开发者意图。落到具体的业务场景,则代码就应该恰到好处干净利落地表现其业务含义。接口体现准确的业务通用语言(ubiquitous language),是DDD的核心价值。

如果我们为这两个搜索条件定义表达业务含义的代数数据类型(algebraic data types),如下代码所示,表意无疑要清晰许多:

  1. sealed trait SearchCriteriaobject SearchCriteria {  
  2. final case object MatchAll extends SearchCriteria  
  3. final case class Contains(s: String) extends SearchCriteria}sealed trait RetailerCriteriaobject RetailerCriteria {  
  4. final case object AnyRetailer extends RetailerCriteria  
  5. final case class Only(retailer: Retailer) extends RetailerCriteria}def searchOffers(  
  6. product: SearchCriteria,  
  7. retailer: RetailerCriteria  
  8. ): Seq[Offer] = ??? 

SearchCriteria与RetailerCriteria作为两个查询条件,分别提供了各自的查询语义,显然要比过分抽象的Some与None更加清晰可读。

引入这样的代数数据类型不仅可以让代码的表意更清晰,还可更好地应对需求的变化。对于现有的SearchCriteria定义而言,倘若要牵强附会,确实可以强词夺理地说:MatchAll就是None的语义,而Contains则对应着Some。然而,如果需求要求增加完全匹配的查询场景,对于Option类型而言,该如何表达?回到SearchCriteria的定义,我们可以轻松地为其增加一种类型:

  1. object SearchCriteria { 
  2. final case object MatchAll extends SearchCriteria 
  3. final case class Contains(s: String) extends SearchCriteria 
  4. final case class Exactly(s: String) extends SearchCriteria} 

比较Option而言,增加了一种新的类型,却极大地提高了代码的可读性,也为代码的未来扩展奠定了基础。与获得的收益相比,仅仅是付出新增类型的微末代价,何足道哉!

【本文为51CTO专栏作者“张逸”原创稿件,转载请联系原作者】

 

戳这里,看该作者更多好文

责任编辑:武晓燕 来源: 51CTO专栏
相关推荐

2017-07-10 13:09:45

前端Flexbox

2021-09-15 16:20:02

Spring BootFilterJava

2016-01-05 11:28:20

按需付费云计算docker

2022-02-21 11:21:40

golang编程语言

2018-01-11 15:31:39

命令Linux关机

2016-05-09 10:41:03

算法分析开发

2017-03-16 11:39:33

Openstack源码姿势

2023-01-30 07:41:43

2020-08-31 06:54:37

注解脱敏ELK

2021-07-12 11:35:13

Go协程Goroutine

2021-10-08 08:38:00

Pipelineshell命令Jenkins

2021-01-08 08:10:34

MySQL表空间回收

2017-10-12 11:30:34

Spark代码PR

2016-12-12 08:48:24

2019-12-27 15:58:57

大数据IT互联网

2018-04-11 15:42:04

开源项目姿势

2024-02-06 14:05:00

Go中间件框架

2019-10-30 17:06:50

AWS物联网IoT

2020-02-24 15:26:40

Python短信免费

2019-01-02 10:49:54

Tomcat内存HotSpot VM
点赞
收藏

51CTO技术栈公众号