Scala - Изброяване срещу Case-Classes

Създадох akka актьор, наречен LogActor. Методът за получаване на LogActors обработва съобщения от други актьори и ги регистрира до определеното ниво на журнал.

Мога да разгранича различните нива по 2 начина. Първият:

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
Така че би било по-добре да направите: case object LogMessage case class ErrorLogMessage(msg : String) разширява LogMessage case class WarningLogMessage(msg : String) разширява LogMessage case class InfoLogMessage(msg : String) разширява LogMessage case class DebugLogMessage(msg : String) разширява LogMessage? Има ли подходящ начин да напишете „msg“ като параметър на LogMessage (така че няма да е необходимо да го пишете във всеки случай клас..)?   -  person    schedule 12.11.2012
comment
можете да направите abstract class LogMessage(msg:String); case class ErrorLogMessage(msg:String) extends LogMessage(msg); ... Но това няма да ви спести от повтаряне на msg. Между другото, това е правилният начин да го направите, наследяването на класове на случаи е силно обезкуражено.   -  person pedrofurla    schedule 12.11.2012


Отговори (3)


Прилага се „Не оптимизирайте преждевременно“. Не вярвам, че разликата между тях изобщо има значение, в сравнение с времето, което прекарвате, предавайки съобщения на вашия актьор или всъщност ги регистрирате. Но очаквам най-доброто представяне би било да се създаде Java enum (която може да бъде лесно достъпна и използвана от Scala) за нива на регистриране вместо Scala Enumeration.

person Alexey Romanov    schedule 12.11.2012

Напълно съм съгласен с Алексей и Денис, че производителността в този случай не трябва да ви притеснява, защото това е по-скоро проблем на оптимизациите на компилатора, а не на разработчика, и не мога да си представя сценарий, при който разликата в производителността може да стане забележима.

Това, което трябва да ви притеснява, е последователността на вашия код и в този смисъл трябва да основавате решението си на това дали искате да се придържате към стария java-ish подход с enums, който е правилно описан в първия ви пример, или напоследък все по-популярния Модел на алгебрични типове данни(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 подход за подобни ситуации, който е доста подобен на този, който насочвате с enums.

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

Една от най-често срещаните операции за регистратор е сравняването на текущото ниво на регистриране с нивото на съобщение и с enum го получавате безплатно, докато с вашата настройка с класове case това ще бъде донякъде обезпокоително. И аз съм съгласен с @AlexeyRomanov: съвпадението не трябва да бъде тясно място тук.

РЕДАКТИРАНЕ: от гледна точка на производителността съвпадението с case класове ще използва instanceof в байт код, докато за enums scala компилатор генерира код, с който нито един от декомпилаторите, които опитах, не може да се справи. Изглежда, че използва equals. Така че технически enum-ите може да са по-бързи, но в действителност няма да има разлика в производителността.

person Denis Tulskiy    schedule 12.11.2012