【问题标题】:Implementing scala trait with property that is class object使用作为类对象的属性实现 scala 特征
【发布时间】:2020-12-03 00:27:56
【问题描述】:

我有一个简单的特质

trait SomeTrait {
  val sourceData: SourceData
}

SourceData 类具有构造函数参数p: Array[String]。 现在,当我在 Object 中扩展这个 trait 时,我们必须为 sourceData 提供实现。

object SomeObject extends SomeTrait {
  override val sourceData: SourceData = ???
  def main(sysArgs: Array[String]){...}
}

但是如果 SourceData 类需要 main 方法中的 sysArgs 怎么办,我如何在 main 方法中覆盖 sourceData,而不是在 SomeObject 的主体中。像这样的:

object SomeObject extends SomeTrait {
  def main(sysArgs: Array[String]){
    override val sourceData: SourceData = new SourceData(sysArgs)
  }
}

我不想使用 var,因为 val 的不变性是首选。而且我还想拥有没有实现的特征,以强制所有子类实现 sourceData。我还有什么其他解决方案?

【问题讨论】:

  • 您可以做的另一件事是将sourceData 作为Option,当调用main 方法时,使用您需要的相关SourceData 创建一个新的SomeObject ,并使用它。根据您使用的术语,我认为这是您的主要课程的一部分。在这种情况下,您不能这样做。但是想想你可以提取到另一个类并在那里做。
  • @TomerShetah SomeObject 是顶级object,因此一旦程序运行,您就无法创建新的。
  • @Tim,您的评论已在我的文章中处理。从问题中不清楚它必须是顶级对象。如果可以将其提取到另一个对象,这种方法可能会奏效。
  • @TomerShetah 我认为它在代码中显示为顶级object,称为SomeObject,它包含main,所以它必须存在于@987654335之前@ 被调用,并且“对象”一词在问题中,很清楚它应该是一个顶级对象。
  • @TomerShetah 如果您确实动态创建它以便它不需要是可变的,那么将值设为 Option 也无济于事。

标签: scala


【解决方案1】:

在这种情况下,您无法避免可变性。 sourceData 在调用main 之前必须有一个值,并且main 必须能够更改该值,因此该值必须是可变的。

一种选择是将sourceData 设为def(无论如何这是个好主意)并让它在SomeObject 中访问private var

trait SomeTrait {
  def sourceData: SourceData
}

object SomeObject extends SomeTrait {
  private var mySource: SourceData = ???
  def sourceData = mySource

  def main(sysArgs: Array[String]) = {
    mySource = new SourceData(sysArgs)
  }
}

这里的根本问题是有一个需要运行时初始化的顶级object。这是必需的,因为SomeObject 是顶级object,可以直接从代码的其他部分访问。

解决方案是依赖注入而不是全局object

trait SomeTrait {
  def sourceData: SourceData
}

object SomeObject {
  case class SomeData(sourceData: SourceData) extends SomeTrait

  def main(sysArgs: Array[String]) = {
    val theSource = SomeData(SourceData(sysArgs))

    // Pass this instance to the rest of the code that needs it
    restOfTheProgram(theSource)
  }
}

其余代码使用传递给它的SomeTrait 实例,而不是直接使用SomeObject

【讨论】:

  • 感谢您的 cmets。我提供的代码只是从一个更复杂的简化的 sn-p。我只是简单地强调了我要解决的问题。但我忘了提到,在 trait 中,很少有额外的抽象方法需要在 SomeObject 中覆盖。
  • 解释我在这里做什么。我们有很多 Spark 工作。每个都需要许多 S3 位置和其他配置。这个位置取决于 sysArgs,所以想法是生成具有属性的类(基于 sysArgs),然后在像这样的作业中使用属性,例如 mySource.inputS3Loc​​ation。这将是不同的路径,具体取决于工作,基本上取决于 sysArgs。
  • @datahack 解决方案是基于 sysArgs 创建一个类并将其传递给需要它的函数,而不是将其放入全局变量中。这称为依赖注入,是软件设计的 SOLID 原则之一。
  • 我肯定错过了一些东西。在此示例中,我如何强制变量覆盖并使用 DI?
【解决方案2】:

在 Scala 中无法做到这一点。您需要有一个类继承 SomeTrait 并从 main 方法实例化它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-10-25
    • 2015-05-26
    • 1970-01-01
    • 1970-01-01
    • 2019-12-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多