Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/typo3/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
使用全局对象或参数传递配置数据,Scala中哪一个更好?_Scala - Fatal编程技术网

使用全局对象或参数传递配置数据,Scala中哪一个更好?

使用全局对象或参数传递配置数据,Scala中哪一个更好?,scala,Scala,我是Scala的新手,我有多年的Java编程经验 通常有两种模式传递一些配置: 使用全局对象听起来像“ConfigManager”。每次我 我需要一个直接从中获取的配置 通过参数传递配置。配置参数可能存在于中 程序中有许多层 我选择一种模式取决于在编写Java时如何使用配置 但在Scala,许多人谈论消除副作用。这让我怀疑是否应该不惜任何代价使用第二种模式 Scala中哪种模式更好?使用全局对象(此对象仅存储只读不可变数据,因此没有问题),它可以同时加载配置对象和配置变量。这比在代码深处加载

我是Scala的新手,我有多年的Java编程经验

通常有两种模式传递一些配置:

  • 使用全局对象听起来像“ConfigManager”。每次我 我需要一个直接从中获取的配置
  • 通过参数传递配置。配置参数可能存在于中 程序中有许多层
我选择一种模式取决于在编写Java时如何使用配置

但在Scala,许多人谈论消除副作用。这让我怀疑是否应该不惜任何代价使用第二种模式

Scala中哪种模式更好?

使用全局对象(此对象仅存储只读不可变数据,因此没有问题),它可以同时加载配置对象和配置变量。这比在代码深处加载配置有很多好处

object ConfigParams {
  val config = ConfigFactory.load()
  val timeInterval = config.getInt("time_interval")
  ....
}
好处:

  • 防止运行时错误(快速失败方法)

    如果您没有拼写任何属性名称,那么在启动过程中,您的应用程序将失败,因为您正急切地尝试获取数据。如果这是在代码库的深处,那么很难知道,当程序的控制转到那一行时,它就会失败。因此,除非进行严格的测试,否则很难检测到

  • 所有配置逻辑和配置转换(如有)的中心位置

    这是所有配置逻辑的中心位置。易于更换和维护

  • 转换可以在不需要重构代码的情况下完成

    object ConfigParams {
      val config = ConfigFactory.load()
      val timeInterval = config.getInt("time_interval")
      ....
    }
    
  • 可维护且可读

  • 易于重构

  • 函数式编程的观点

    是的,从Fail fast的角度来看,急切地加载配置文件是一个好主意,但这不是一个好的函数式编程实践

    但重要的是,在加载应用程序的过程中,不要将副作用与任何其他逻辑混为一谈,并将其分开。因此,当您在项目开始时隔离副作用和副作用时,这将不是一个计划

    一旦副作用完成并启动应用程序。您的纯代码库不会因此受到影响,并保持纯净和干净。因此,尽管它有副作用,但它是孤立的,不会影响您的代码库。值得您再次体验的好处,请继续。

    全局对象不好:

    使每个组件将其配置(单个部件)作为构造函数参数(可能有一些默认值)。防止创建无效组件或未配置的组件

    您可以在单个类中收集配置值的初始处理,以集中配置代码,并在缺少内容时快速失败。但不要让组件(需要配置的类)依赖于全局对象,也不要将整个配置作为参数。正是它们作为构造函数参数所需要的

    例如:

    // centralize the parsing of configuration
    case class AppConfig (config: Config) {
      val timeInterval = config.getInt("type_interval")
      val someOtherSetting = config.getString("some_other_setting")
    }
    
    ...
    // don't depend on global objects
    class SomeComponent (timeInterval: Int) {
      ...
    }
    
    object SomeApplication extends App {
      val config = AppConfig(ConfigFactory.load())
    
      val component = new SomeComponent(config.timeInterval)
    }
    

    我怀疑使用companion object会解决很多问题:作为参考:
    Reader
    monad(由Cats或Scalaz提供)可以简化第二种模式,但如果您刚开始使用Scala,这可能有点高级。有几个
    Reader
    免费
    @Inject
    通过guice或类似工具,@PeterNeyens感谢您的建议!在我看到你的评论后,我读了一些关于读者monad的帖子。我想我现在知道怎么用了。但我也认为使用它来传递包含不同方面的值的全局配置可能不是一个好主意。这可能会导致在程序的较低级别中携带太多的配置,或者我应该以使编程更加复杂的方式削减配置。无论如何,我将使用Reader monad编写一些代码,看看发生了什么。@AndrewLi大多数
    Reader
    实现都有某种
    local
    函数,它允许您从全局配置转到本地配置,例如参见(
    Reader[a,B]
    等于
    Kleisli[Id,a,B]中的最后一个示例
    )。这不会很快失败,因为对象初始化是在对象第一次请求字段时发生的,这可能不是在实际启动时发生的(例如,如果您在Play中使用Guice DI,则可能会在第一次请求时失败,如果需要该值的话)。此外,对象初始化可能会失败,将来的引用将无法找到该类。通过将可能出现故障的字段定义设置为
    lazy val
    ,可以稍微改善这种极端故障模式。此外,这是不可避免的单例状态,它是不灵活的(例如,测试时很难使用)。