我有一些需要传递的数据,这些数据在处理后需要得到确认。忽略确认,我会编写类似的代码
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)
这有一些问题。
unapply
方法将解压 ack
字段,所以我需要 case data @ Data(x, y, _)
。ack
字段。acknowledge
。如果
ack
字段确实是私有的,那么前两个问题就可以解决。我看到的解决方案是手动定义 unapply
方法,或者使 Data
成为一个普通类并实现案例类自动提供给我的大部分方法。
还有更好的选择吗?
我认为解决第三个问题是一个单独的问题,但我不会反对快速指向解决它的设计模式。
虽然我不同意“将逻辑与数据混合”总是一个坏主意,但正如评论中所建议的那样(“封装” - 将数据和操作它的逻辑合并到一个对象中 - 是面向对象的特征之一编程,为什么最近有点失宠,即使在 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))