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

Как войти в систему Scala * без * ссылки на регистратор в * каждом экземпляре *?

Я посмотрел пример регистрации в Scala, и он обычно выглядит так:

import org.slf4j.LoggerFactory

trait Loggable {
  private lazy val logger = LoggerFactory.getLogger(getClass)
  protected def debug(msg: => AnyRef, t: => Throwable = null): Unit =
    {...}
}

Это кажется независимым от конкретной структуры каротажа. Хотя это и делает эту работу, она также вводит посторонний ленивый val в каждом экземпляре, который хочет вести журнал, что вполне может быть каждым экземпляром всего приложения. Это кажется мне слишком тяжелым, особенно если у вас много "маленьких экземпляров" определенного типа.

Есть ли способ поставить логгер в объект конкретного класса вместо этого, просто используя наследование? Если я должен явно объявить регистратор в объекте класса и явно ссылаюсь на него из класса /trait, тогда я написал почти такой же код, как если бы я вообще не использовал его повторно.

Выраженный в конкретном контексте без регистрации, проблема будет следующей:

Как объявить в признаке, что класс реализации должен иметь одноэлементный объект типа X и что этот одноэлементный объект должен быть доступен через метод def x: X?

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

4b9b3361

Ответ 1

Как объявить в признаке, что класс реализации должен иметь одиночный объект типа X, и что этот одноэлементный объект должен быть доступный с помощью метода def x: X?

Объявить черту, которая должна быть реализована вашими сопутствующими объектами.

trait Meta[Base] {
  val logger = LoggerFactory.getLogger(getClass)
}

Создайте базовый признак для ваших классов, подклассы должны перезаписать мета-метод.

trait Base {
  def meta: Meta[Base]
  def logger = meta.logger
}

Класс Все, что сопутствующий объект:

object Whatever extends Meta[Base]

class Whatever extends Base {
  def meta = Whatever

  def doSomething = {
    logger.log("oops")
  }
}

Таким образом вам нужно только иметь ссылку на метаобъект.

Мы можем использовать любой класс, подобный этому.

object Sample {
  def main(args: Array[String]) {
    val whatever = new Whatever
    whatever.doSomething
  }
}

Ответ 2

Преждевременная оптимизация - это корень всех злых

Сначала давайте сначала разобраться в одном: если ваша черта выглядит примерно так:

trait Logger { lazy val log = Logger.getLogger }

Тогда то, что вы сделали не, выглядит следующим образом:

  • У вас НЕ создан экземпляр журнала на один экземпляр вашего типа
  • Вы не дали себе ни памяти, ни проблемы с производительностью (если у вас нет)

Вы сделали следующее:

  • У вас есть дополнительная ссылка в каждом экземпляре вашего типа
  • При первом доступе к журналу вы, вероятно, выполняете поиск по карте.

Обратите внимание, что даже если вы создали отдельный регистратор для каждого экземпляра вашего типа (что я часто делаю, даже если моя программа содержит сотни тысяч из них, так что у меня есть очень мелкий контроль над моим протоколированием), у вас почти наверняка не будет ни производительности, ни проблемы с памятью!


Одно "решение" (конечно), чтобы заставить объект-компаньон реализовать интерфейс регистратора:

object MyType extends Logger

class MyType {
  import MyType._
  log.info("Yay")
}

Ответ 3

Я не уверен, что полностью понимаю ваш вопрос. Поэтому я приношу свои извинения, если это не тот ответ, который вы ищете.

Определите object, если вы положили свой logger, а затем создайте компаньон trait.

object Loggable {
   private val logger = "I'm a logger"
}

trait Loggable {
   import Loggable._
   def debug(msg: String) {
      println(logger + ": " + msg)
   }
}

Итак, теперь вы можете использовать его следующим образом:

scala> abstract class Abstraction
scala> class Implementation extends Abstraction with Loggable
scala> val test = new Implementation
scala> test.debug("error message")
I'm a logger: error message

Отвечает ли это на ваш вопрос?

Ответ 4

Я думаю, вы не можете автоматически получить соответствующий одноэлементный объект класса или потребовать, чтобы такой синглет существовал.

Одна из причин заключается в том, что вы не можете знать тип синглтона до его определения. Не уверен, если это поможет или если это лучшее решение вашей проблемы, но если вы хотите, чтобы какой-то метаобъект определялся с определенным признаком, вы могли бы определить что-то вроде:

trait HasSingleton[Traits] {
  def meta: Traits
}

trait Log {
  def classname: String
  def log { println(classname) }
}

trait Debug {
  def debug { print("Debug") }
}

class A extends HasSingleton[Log] {
  def meta = A // Needs to be defined with a Singleton (or any object which inherits from Log}
  def f {
    meta.log
  }
}
object A extends Log {
  def classname = "A"
}

class B extends HasSingleton[Log with Debug] { // we want to use Log and Debug here
  def meta = B
  def g {
    meta.log
    meta.debug
  }
}
object B extends Log with Debug {
  def classname = "B"
}

(new A).f
// A
(new B).g
// B
// Debug