在Scala的case类和类字段中使用Optional是否有代码味道?

Mat*_*zcz 8 java oop functional-programming scala

在Stackoverflow上已经有很多关于在Java 中使用Optional的正确方法的讨论(像这样的讨论,或者这个)

截至目前,在Java中使用Optional作为类成员被广泛认为是一种代码气味,甚至因故意不实现Serializable接口而气馁.此外,我们应该在DTO,构造函数和方法的输入参数中避免它.从视图中的一切OOP点我迄今大约读可选吸引我的原因.

我的问题是,Scala的FP方面是否会以某种方式改变某些东西我们应该使用Optional?特别是因为Scala 中Optional的实现似乎更丰富.我发现很多文章描述了如何在Scala中使用它,但是没有一篇文章在我应该使用它时何时耗尽主题,何时不应该使用它.

jub*_*0bs 9

简短的回答

Option字段有用例; 他们本质上并不坏.然而,即使几个成熟的库(例如ScalaTest)定义了具有Option字段的类,后者IMO也往往是代码气味,因为它们经常试图为自己的好处做太多.

在许多情况下,包含可选字段的类型可以容易且有利地由代数数据类型替换.

一个例子

域名

考虑处理帐户的业务域.一个帐户开始其生命有一天,当一个开放的帐户,但最终可能会被关闭.除其他数据外,账户包含其开放和结束的日期(如果适用).

使用Option字段

以下是使用Option字段的帐户实现:

final case class Account(openedOn: LocalDate, closedOn: Option[LocalDate], ...)
Run Code Online (Sandbox Code Playgroud)

我们还有一个帐户服务,除其他外,还定义了一种close方法:

trait AccountService {
  // ...
  def close(account: Account): Account
}
Run Code Online (Sandbox Code Playgroud)

出于多种原因,这种方法存在问题.一个问题是Account不是特别高效:因为它closedOn是一个"盒装"类型,你可以说有一个间接级别太多了.而且,Account内存占用空间不太理想:"封闭账户"包含一个非常无趣的价值(None),这是浪费空间.

另一个更严重的问题是该close方法不能在类型级别强制执行参数为"开户"并且结果为"已关闭帐户".您必须编写测试以检查您的实现是否强制执行此业务规则.

使用小型ADT(和避开Option字段)

考虑以下替代设计:

sealed trait Account { ... }

final case class OpenAccount(openedOn: LocalDate, ...) extends Account

final case class ClosedAccount(openedOn: LocalDate, closedOn: LocalDate, ...) extends Account
Run Code Online (Sandbox Code Playgroud)

这个小型ADT可以解决性能问题,但还有更多...您现在可以在类型级别对业务规则进行编码!这是使非法国家无法代表的一个例子(Yaron Minsky的一句话).因此,您服务的API变得更具表现力,更难以滥用:

trait AccountService {
  // ...
  def close(account: OpenAccount): ClosedAccount
}
Run Code Online (Sandbox Code Playgroud)

这个例子可能足以让你相信第二种方法更可取,并且Option最好避免使用这些字段(或者至少要谨慎使用).

资源

有关消除可选字段以使非法国家无法代表的更多信息,请参阅


rog*_*one 8

scala 选项实现Serializable

的使用OptionScala中强烈建议为空的属性.Option[T]被认为T比前者比后者更安全.

截至目前,在Java中使用Optional作为类成员被广泛认为是代码气味

相反null,scala中的Optional属性的原位存在被认为是代码气味.

尽管Scala是一种功能性语言,但它也是一种促进类型安全的语言.在理想世界中,真正完全类型安全的语言不会有运行时异常,NullpointerException并且Option在Scala中扮演重要角色以避免它.

Option [T]表示属性可以处于null(即None)状态,并强制属性的客户端处理该null场景.因此,Option会向类型系统添加更多信息,并使代码更加类型安全.

使用模式匹配和Monad/Monoid等语言功能,Scala中使用Optional数据类型的经济性在Scala中非常便宜且用户友好,与Java相比.

模式匹配:

 optionalVariable match {
   case Some(x) => /* handle when variable has a value*/
   case None => /* handle when the variable doesn't have a value*/
 }
Run Code Online (Sandbox Code Playgroud)

Monad选项:

optionalVariable foreach { x => /* run code when the variable is defined*/ }
optionalVariable map { x => /* map optional type to another type */}
Run Code Online (Sandbox Code Playgroud)

编辑:

Jubobs非常好用,可以用自定义类型替换使用Option.但我认为还有更多的情况,其中可选属性更有意义.为例如:如果帐户对象具有可选的属性,如emailId和phoneNo则选项[T]是因为对于每个组合将是不切实际的,并会导致爆炸类创建自定义类型的一个更好的解决方案.

  • 那没关系.拥有所有属性的数据类型是可选的在设计级别是有问题的,无论用于编码单个可选属性的方式如何. (3认同)