需要私有数据的 scala 案例类样式对象的最佳设计

问题描述 投票:0回答:1

我有一些需要传递的数据,这些数据在处理后需要得到确认。忽略确认,我会编写类似的代码

case class Data(x: Int, y: String)

def process(msg: Any): Unit = 
  msg match 
    case Data(x, y) => processData(x, y)
    case Other(z) => processOther(z)

要添加确认,我的第一个想法是添加发送确认所需的数据:

case class Data(x: Int, y: String, private ack: Acknowledger):
  def acknowledge: Unit = ack.complete(this)

def process(msg: Any): Unit = 
  msg match 
    case data @ Data(x, y) => 
       processData(x, y)
       data.acknowledge
    case Other(z) => processOther(z)

这有一些问题。

  • 正如为什么 scala 允许私有 case 类字段? 中所讨论的,生成的
    unapply
    方法将解压
    ack
    字段,所以我需要
    case data @ Data(x, y, _)
  • 有人可能会尝试直接使用
    ack
    字段。
  • 用户可能会忘记拨打
    acknowledge

如果

ack
字段确实是私有的,那么前两个问题就可以解决。我看到的解决方案是手动定义
unapply
方法,或者使
Data
成为一个普通类并实现案例类自动提供给我的大部分方法。

还有更好的选择吗?

我认为解决第三个问题是一个单独的问题,但我不会反对快速指向解决它的设计模式。

scala case-class
1个回答
0
投票

虽然我不同意“将逻辑与数据混合”总是一个坏主意,但正如评论中所建议的那样(“封装” - 将数据和操作它的逻辑合并到一个对象中 - 是面向对象的特征之一编程,为什么最近有点失宠,即使在 scala 中仍然占有一席之地),你试图做的事情似乎违反了单一责任原则(在我看来,这是一个非常重要的原则)更重要的问题)。

如果您的课程的目的是传递消息,则它不应该有责任确认收到。确认是接收消息的人的工作。所以,我认为这样的事情最有意义:

case class Data(x: Int, y: String) 

def process(d: Data, ack: Acknowldger) = msg match { 
  case Data(x, y) => 
     processData(x, y)
     ack.complete(data)
}

让我们看看这种方法是否能解决您的担忧:

生成的 unapply 方法将解压 ack 字段

已解决(不再有 ack 字段)

有人可能会尝试直接使用 ack 字段。

也解决了(不再有 ack 字段),但根据记录,我不认为这是一个真正的问题......它甚至意味着“有人可能会尝试......”?就像,尝试做什么?发送一个确认?好吧,他们可以使用你的确认方法来达到同样的效果吗?为什么他们会选择直接使用ack?只是为了激怒你吗? :D 实际上,我不喜欢在 scala 中使用

private
字段,至少对于不可变成员和对象,99% 的情况应该是这样,因为这正是原因:它们不必要地使设计复杂化,并使其灵活性降低零利益。

用户可能会忘记调用确认。

这实际上是您原始设计的唯一真正问题,并且是重要的问题,使该设计变得“糟糕”。使用我建议的方法,可以通过单元测试轻松解决这个问题:

val ack = mock[Acknowledger]
val data = Data(1, "foo")
process(data, ack)
verify(ack.complete(data))
© www.soinside.com 2019 - 2024. All rights reserved.