Подтвердить что ты не робот

Scala - Перечисление по классам классов

Я создал акк-актера под названием LogActor. LogActors получает метод обработки сообщений от других участников и регистрации их до указанного уровня журнала.

Я могу различать разные уровни двумя способами. Первый:

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 или соответствовать значению перечисления?

(Я читаю этот вопрос, но нет ответа, относящегося к проблеме времени выполнения)

4b9b3361

Ответ 1

"Не оптимизировать досрочно". Я не считаю, что разница между ними, вероятно, будет иметь значение, по сравнению с тем временем, когда вы тратите сообщения на своего актера или фактически регистрируете их. Но я ожидаю, что лучшая производительность будет заключаться в создании Java enum (к которому можно легко получить доступ и использовать из Scala) для уровней ведения журнала вместо Scala Enumeration.

Ответ 2

Я полностью согласен с Алексеем и Деннисом в том, что производительность в этом случае не должна вас беспокоить, потому что это больше проблема оптимизации компилятора, а не разработчика, и я не могу представить сценарий, когда разница в производительности может стать заметной,

Что должно вас беспокоить, так это ваша последовательность кода, и в этом смысле вы должны основывать свое решение на том, хотите ли вы придерживаться старого подхода 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 )

Ответ 3

Одной из наиболее распространенных операций для регистратора является сравнение текущего уровня ведения журнала с уровнем сообщения, а с перечислениями вы получаете его бесплатно, а при настройке с классами классов это будет несколько хлопотно. И я согласен с @AlexeyRomanov: сопоставление не должно быть узким местом здесь.

EDIT: по производительности, совпадение с классами case будет использовать instanceof в байт-коде, а для enums scala код сгенерированного компилятором, который ни один из декомпиляторов, которые я пытался, не мог справиться. Казалось, что он использует equals. Так что технически перечисления могут быть быстрее, но на самом деле не будет никакой разницы в производительности.