前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >使用Option的正确姿势

使用Option的正确姿势

作者头像
张逸
发布2018-03-07 17:09:05
1K0
发布2018-03-07 17:09:05
举报
文章被收录于专栏:斑斓斑斓

我们会频繁地使用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:

代码语言:javascript
复制
def searchOffers(
  productTitle: Option[String],
  retailer: Option[Retailer]
  ): Seq[Offer] = ???

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

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

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

代码语言:javascript
复制
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更加清晰可读。

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

代码语言:javascript
复制
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而言,增加了一种新的类型,却极大地提高了代码的可读性,也为代码的未来扩展奠定了基础。与获得的收益相比,仅仅是付出新增类型的微末代价,何足道哉!

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2017-02-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 逸言 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档