甚至避免选项字段。对于 String 始终为空字符串,对于 Int 可选字段始终为 0

Avoid even Option fields. Always empty string for String and 0 for Int optional fields

我有基于 JSON 和 Play Framework 的 Scala REST 服务。 JSON 的某些字段是可选的(例如 middleName)。我可以将其标记为选项,例如

 middleName: Option[String]

甚至不要指望 JSON。但我想避免将来可能出现的应用程序错误并简化生活。如果用户不想提供此信息并且在整个应用程序中没有选项字段(JSON/DB 开销很小),我想将其标记为可预期但为空。

在整个应用程序中避免选项字段是个好主意吗?如果 String 字段为空,则它包含一个空字符串,但必须出现在 JSON/DB 中。如果 Int 字段为空,则它包含 0 etc

提前致谢

我认为这取决于您的业务逻辑以及您希望如何使用这些值。

middleName 的情况下,我假设您主要使用它以个人方式称呼用户,您只需连接 titlefirstNamemiddleNamelastName。因此,无论用户是否指定,您都将完全相同地对待该值。所以我认为使用空字符串而不是 None 可能更可取。

如果值 0"" 是您的业务逻辑的有效值,我会选择 Option[String],同样在您拥有的情况下不同的行为取决于是否指定值。

x match {
  case 0 => foo
  case _ => bar(_) 
}

描述性差
x match {
  case Some(i) => bar(i)
  case None => foo
}

我想你会因为失去类型安全而后悔避免使用 Option。如果您传递潜在的空对象引用,每个接触它们的人都必须记住检查空值,因为没有任何东西可以强制他们这样做。不记得是 NullPointerException 等待发生。 Option 的使用强制代码处理没有价值的可能性;忘记这样做会导致编译错误:

case class Foo(name: Option[String])
...
if (foo1.name startsWith "/")  // ERROR: no startsWith on Option

我偶尔会在非常本地化的代码中使用空值,我认为要么性能很关键,要么我有很多很多对象但不想拥有所有这些 SomeNone 对象占用内存,但我绝不会在 public API 中泄漏 null。使用空值是一种复杂的优化,只有在避免灾难所需的额外警惕被收益证明是合理的情况下才应使用。这种情况很少见。

我不确定我是否理解您对 JSON 的需求,但听起来您可能希望 Option 字段不会从 JSON 文档中消失。在 Spray-json 中有一个 NullOptions 特性专门用于此。您只需将它混合到您的协议类型中,它会影响其中定义的所有 JsonFormat(如果您愿意,您可以将其他协议类型 "not" 混合),例如

trait FooJsonProtocol extends DefaultJsonProtocol with NullOptions {
  // your jsonFormats
}

如果没有NullOptionsOption个值为None的成员将被完全省略;有了它,它们出现 null 值。我认为如果您显示具有空值的可选字段而不是让它们消失,对用户来说会更清楚,但为了传输效率,您可能希望将它们省略。有了Spray-json,至少,你可以pick.

我不知道其他 JSON 软件包是否有类似的选项,但如果出于某种原因您不想使用 Spray-json,这也许会帮助您寻找它(顺便说一下,现在速度非常快)。

这是个坏主意,因为通常您想以不同的方式处理缺少的内容。如果传递一个 "" 或 0 值,这很容易与真实值混淆;您最终可能会发送一封以 "Dear Mr ," 开头的电子邮件或祝他们 35 岁生日快乐,因为时间戳 0 出现在 1970 年 1 月 1 日。如果您在代码和类型系统,这会迫使您考虑是否实际设置了一个值,如果没有设置您想要做什么。

也不要盲目地将 Option 推到任何地方。如果未提供值是一个错误,您应该立即检查并尽快抛出错误,而不是等到应用程序中的很晚才更难调试 None 的来源.

它不会让你 "life easier"。如果有的话,它会让事情变得更难,而不是避免应用程序错误反而会增加它们的可能性。您的应用程序代码必须充满 if(middleName != "") { doSomething(middleName); }if(age == 0) "Unknown age" else age.toString 等检查,您将不得不依赖程序员记住以特殊方式处理这些 "kinda-optional" 字段。

所有这些你都可以 "for free" 使用 Option 的单子属性和 middleName.foreach(doSomething)age.map(_.toString).getOrElse("")