除了已经给出的答案:
Option
类型并不总是唯一的选择。并且让大多数接口返回 anOption
当然是不可取的。通常,仅在调用者可以期望方法有时返回“无”的情况下才需要。
设计一个好的 API 需要更多的思考。看看你的特质和职业。如果他们不能提供这个或那个财产,他们是否完整?- 如果没有某个属性它们是不完整的,那么该属性不应该是一个Option
值。相反,您可能会说:如果无法提供该属性,那么该对象将属于不同的类型。
举个例子,考虑一个代表棋盘游戏地图的网格。每个字段都由一个Cell
数据类型表示。一些单元格可能有颜色。
API 的第一个版本可能如下所示:
trait Cell {
def color:Color
}
现在,在某些时候,您会注意到某些单元格没有颜色。例如,空单元格。或者只包含文本的单元格,这些文本应该以默认的 GUI 颜色呈现。
所以你最终会考虑这个版本:
trait Cell {
def color:Option[Color] = None
}
现在,如果需要,每个Cell
实现都可以自由地覆盖该color
属性。但这不是唯一可能的解决方案。
想想这个替代方案:
trait Cell { }
trait ColoredCell extends Cell {
def color:Color
}
现在,单元格的类型决定了它是否有颜色,有色单元格必须有颜色。
UI 层上的一些代码可能包含这样的片段:
...
val cell:Cell = grid cellAt coordinates
val uiComponent:JComponent = ...
cell match {
case coloredCell:ColoredCell => uiComponent setColor coloredCell.color
case _ => // No color assigned
}
这种方法最适用于不可变对象。它们永远不会改变,而是返回一个代表修改后的版本的新实例。
例如,每个单元格可能都有一个设置颜色的方法:
trait Cell {
def withColor(color:Color):ColoredCell
}
它返回一个新单元格,它表示该单元格的副本,具有不同的颜色。当然,我们已经知道这将是 的一个实例ColoredCell
,所以我们将它放在方法契约中。
有时,这种方法可以很好地工作,但您应该事先仔细检查它与您的模型的匹配程度。