如何在Scala中对对象进行全局访问而不将其作为单例或将其传递给所有对象?

Fel*_* Mc 0 singleton scala

我有一个Logger类,可以在我的应用程序中记录事件.虽然我在这个应用程序中只需要一个记录器实例,但我希望这个类可以重用,所以我不想让它成为单例并将它与我对该应用程序的特定需求结合起来.

我希望能够从应用程序的任何位置访问此Logger实例,而无需每次都创建一个新的实例或将其传递给可能需要记录某些内容的每个类.我目前所做的是有一个ApplicationUtils单例,我用它作为应用程序Logger的访问点:

object ApplicationUtils {
    lazy val log : Logger = new Logger()
}
Run Code Online (Sandbox Code Playgroud)

然后我有一个Loggable特性,我添加到需要Logger的类:

trait Loggable {
    protected[this] lazy val log = ApplicationUtils.log
}
Run Code Online (Sandbox Code Playgroud)

对于我想要实现的目标,这是一种有效的方法吗?感觉有点黑客.我可以使用更好的方法吗?我对Scala很新.

Ran*_*ulz 5

将功能放入objects 时要小心.该功能很容易测试,但如果您需要测试该代码的客户端以确保它们正确地与它进行交互(通过模拟和间谍),那么您就会陷入困境,导致对象编译为最终类,因此无法模拟.

相反,使用此模式:

trait T { /* code goes here */ }

object T extends T /* pass this to client code from main sources */
Run Code Online (Sandbox Code Playgroud)

现在,您可以trait T在测试代​​码中创建Mockito模拟/间谍,将其传递并确认测试代码与代码的交互trait T是它们应该是什么.

如果您的代码是T的客户端并且与其的交互不需要测试,则可以直接引用object T.


Joh*_*ahl 5

要解决您尝试做的事情(而不是您要问的问题),请查看TypeSafe的scalalogging包.它提供了一个Logging你可以使用的特性:

class MyClass extends Logging {
  logger.debug("This is very convenient ;-)")
}
Run Code Online (Sandbox Code Playgroud)

它是SLF4J的基于宏的包装器,所以像logger.debug(...)编译为if (logger.isDebugEnabled) logger.debug(...).