【问题标题】:Scala - Enumeration vs. Case-ClassesScala - 枚举与案例类
【发布时间】:2012-11-12 12:12:20
【问题描述】:

我创建了名为 LogActor 的 akka actor。 LogActor 的 receive 方法处理来自其他 Actor 的消息并将它们记录到指定的日志级别。

我可以通过两种方式区分不同的级别。第一个:

import LogLevel._
object LogLevel extends Enumeration {
    type LogLevel = Value
    val Error, Warning, Info, Debug = Value
}
case class LogMessage(level : LogLevel, msg : String) 

第二个:(EDIT)

abstract class LogMessage(msg : String)
case class LogMessageError(msg : String) extends LogMessage(msg)
case class LogMessageWarning(msg : String) extends LogMessage(msg)
case class LogMessageInfo(msg : String) extends LogMessage(msg)
case class LogMessageDebug(msg : String) extends LogMessage(msg)

哪种方式更有效?匹配case类或匹配枚举值是否需要更少的时间?

(我阅读了this 问题,但没有任何关于运行时问题的答案)

【问题讨论】:

  • 要记住的重要一点:在您的(可能过于简化的)第二个 sn-p 中,您没有所有 xxxLogMessage 案例类共有的基类。
  • 那么这样做会更好:case object LogMessage case class ErrorLogMessage(msg : String) extends LogMessage case class WarningLogMessage(msg : String) extends LogMessage case class InfoLogMessage(msg : String) extends LogMessage case类 DebugLogMessage(msg : String) 扩展 LogMessage?是否有适当的方法将“msg”写为 LogMessage 的参数(因此不必在每个案例类中都写它..)?
  • 你可以这样做abstract class LogMessage(msg:String); case class ErrorLogMessage(msg:String) extends LogMessage(msg); ...,但这不会让你免于重复msg。顺便说一句,这是正确的做法,强烈建议不要继承案例类。

标签: scala enums akka actor case-class


【解决方案1】:

我完全同意 Alexey 和 Dennis 的观点,即这种情况下的性能不应该打扰您,因为这更多是编译器优化的问题,而不是开发人员的问题,而且我无法想象性能差异会变得明显的场景.

困扰你的是你的代码一致性,从这个意义上说,你应该根据你的决定是要坚持使用第一个示例中正确描述的枚举的旧 java-ish 方法,还是最近越来越流行的代数数据类型 (ADT) 模式。后者你试图在你的第二个例子中表示,但有一些错误。

以下是如何正确使用 ADT 模式解决问题。

ADT 解决方案 #1

// 1. marked `sealed` to make pattern matching exhaustive
// 2. used a trait to avoid double storage of msg` and 
//    make the inheritance easier
sealed trait LogMessage { def msg : String }
// A better solution for isolation than names like "LogMessageError".
// Allows you to either address the members with a namespace like 
// "LogMessage.Error" or do "import LogMessage._" and address them 
// directly
object LogMessage { 
  case class Error (msg : String) extends LogMessage
  case class Warning (msg : String) extends LogMessage
  case class Info (msg : String) extends LogMessage
  case class Debug (msg : String) extends LogMessage
}

ADT 解决方案 #2

抱歉,您可能会弄乱您的头脑,但值得注意的是,对于类似情况,还有一种替代 ADT 方法,这有点类似于您使用枚举的方法。

sealed trait LogLevel 
object LogLevel {
  case object Error extends LogLevel
  case object Warning extends LogLevel
  case object Info extends LogLevel
  case object Debug extends LogLevel
}
case class LogMessage ( level : LogLevel, msg : String )

【讨论】:

    【解决方案2】:

    “不要过早优化”适用。与您将消息传递给您的演员或实际记录它们所花费的时间相比,我认为它们之间的差异根本不重要。但我希望最好的性能是为日志级别创建 Java enum(可以从 Scala 轻松访问和使用)而不是 Scala Enumeration

    【讨论】:

      【解决方案3】:

      记录器最常见的操作之一是将当前记录级别与消息级别进行比较,使用枚举可以免费获得它,而使用案例类的设置会有些麻烦。我同意@AlexeyRomanov 的观点:匹配不应该是这里的瓶颈。

      编辑:在性能方面,与案例类匹配将在字节码中使用 instanceof,而对于枚举,scala 编译器生成的代码是我尝试过的任何反编译器都无法处理的。它似乎在使用equals。因此,从技术上讲,枚举可能会更快,但实际上在性能上不会有任何差异。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-04-22
        • 1970-01-01
        • 2015-11-16
        • 1970-01-01
        • 2018-12-14
        • 1970-01-01
        相关资源
        最近更新 更多