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) 

Второй: (ИЗМЕНИТЬ)

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

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


person Community    schedule 12.11.2012    source источник
comment
Следует иметь в виду одну важную вещь: в вашем (возможно, упрощенном) втором фрагменте у вас нет базового класса, общего для всех xxxLogMessage классов case.   -  person Malte Schwerhoff    schedule 12.11.2012
comment
Так что было бы лучше: LogMessage? Есть ли подходящий способ записать "msg" в качестве параметра LogMessage (чтобы не было необходимости писать его для каждого класса ...)?   -  person    schedule 12.11.2012
comment
вы могли бы сделать abstract class LogMessage(msg:String); case class ErrorLogMessage(msg:String) extends LogMessage(msg); ..., но это не спасет вас от повторения msg. Кстати, это правильный способ сделать это, наследование case-классов крайне не рекомендуется.   -  person pedrofurla    schedule 12.11.2012


Ответы (3)


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

person Alexey Romanov    schedule 12.11.2012

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

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

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

РЕДАКТИРОВАТЬ: с точки зрения производительности сопоставление с классами case будет использовать instanceof в байт-коде, в то время как для перечислений компилятор scala сгенерировал код, который ни один из декомпиляторов, которые я пробовал, не мог обработать. Похоже, он использовал equals. Так что технически перечисления могут быть быстрее, но на самом деле разницы в производительности не будет.

person Denis Tulskiy    schedule 12.11.2012