【问题标题】:Scala Algebraic Data Types for file extension to format mapping用于文件扩展名到格式映射的 Scala 代数数据类型
【发布时间】:2014-01-14 16:29:38
【问题描述】:

所以我是一个完全的 Scala 菜鸟,来自生疏的 Java 背景和主要是 PHP+JavaScript 的背景。 我发现自己很沮丧,因为我想在 Scala 中实现一些设计模式,但我不知道如何实现,这会导致大量阅读,而不是任何编程。

不管怎样,我正忙着制作一个基本程序来学习细节,我正在制作一个配置文件阅读器。我需要做的是将不同的格式映射到扩展,诀窍是不同的扩展,如yamlyml 映射到相同的格式。

在 java 中,我将使用 Enums 并控制构造函数中的映射。很直接。

在 scala 中...不是那么多。首先,我发现 ADT(意思是代数数据类型而不是抽象数据类型)是比使用枚举更好的选择(原因我仍然不清楚),并且它与 Java 库一起工作得更好(具有讽刺意味的是,我没有知道为什么。)

接下来我发现了 ADT 的各种格式,作为初学者同时掌握所有概念是很费力的。我已经设法弄清楚companion Objects 类似于 javascript,因为它是一个几乎像单例的单个实例化,并且允许您在静态上下文中访问定义的函数。


现在回到问题

我写了我认为是个好主意的东西,但是代码看起来很麻烦,冗长,而且我无法弄清楚一些用例,而且从我所有的 JS 插件编写中可以看出我有点命名空间螺母,在逻辑分离方面做得很好,但在 scala 中不简洁。

class ConfigManager {
  object Formats{
    case class Format(f: String){
      val format = f.toUpperCase()
    }
    case object Yaml extends Format("YAML")
    case object Xml  extends Format("XML")
    case object Json extends Format("JSON")
  }

  object Extensions {
    case class Extension(e: String) {
      val extension = e.toUpperCase()
    }
    case object Yaml extends Extension("YAML")
    case object Yml  extends Extension("YML")
    case object Xml  extends Extension("XML")
    case object Json extends Extension("JSON")
  }


  object Config {
    private sealed trait ConfigFormats {
      def f: Formats.Format
      def e : Extensions.Extension
    }
    case class ConfigFormat(f: Formats.Format, e: Extensions.Extension) 
            extends ConfigFormats (f, e)

    case object Yaml1 extends ConfigFormat(Formats.Yaml, Extensions.Yaml)
    case object Yaml2 extends ConfigFormat(Formats.Yaml, Extensions.Yml)
    case object Xml   extends ConfigFormat(Formats.Xml, Extensions.Xml)
    case object Json  extends ConfigFormat(Formats.Json, Extensions.Json)
  }

  //this looks nice, and useful, is concise and does what I want
  def readConfig(c: Config.ConfigFormat){
    c.f match {
      case Formats.Yaml => readConfigYaml()
      case Formats.Json => readConfigJson()
      case Formats.Xml  => readConfigXml()
      case _ => throw new Exception("Invalid Config format")
    }
  }

  //and this is where I lose the plot
  def setFormat(extension: String){
    val configFormat = new Extensions.Extension(discoveredFormat) match {
      case Config.????? => ????
    }
  }
}

所以显而易见的问题是我的实现到底有多糟糕,我应该怎么做,为什么? (我最关心的是“为什么”)

【问题讨论】:

  • 你应该使用包而不是对象。然后使用多态性。也许你不需要这么多类型来实现你的目标。
  • 如果你想保持功能,试着让你的方法返回一个值而不是产生副作用。
  • 许多设计模式在 Scala 中没有用,因为它们解决的问题已经消失了。
  • 所以我已经意识到,但这并不意味着scala中的“正确”方式自然而然。说到这一点,我仍然很想知道 ADT 的实现,以及为什么。

标签: scala case-class algebraic-data-types


【解决方案1】:

Scala 枚举很好,你可以像这样定义 FormatsExtensions

object Formats extends Enumeration {
  type Eny = Value
  val YAML, XML, JSON = Value
}
object Extensions extends Enumeration {
  type Eny = Value
  val YAML, YML, XML, JSON = Value
}

如果你想要一个map,只需使用一个map

val configFormats: Map[Extensions.Value, Formats.Value] = Map(
  Extensions.YAML -> Formats.YAML,
  Extensions.YML  -> Formats.YAML,
  Extensions.XML  -> Formats.XML,
  Extensions.JSON -> Formats.JSON)

那么我不太确定你的方法应该做什么,但看起来你的setFormat 可以这样开始:

def setFormat(extension: String): Unit = {
  val format: Formats.Value = configFormats(Extensions withName extension)
  ...
}

你看,就像 Java 一样 :) 如果你能更好地描述你正在尝试做的事情,我们可能会给你一个更“惯用的方式”。

【讨论】:

  • 我无法要求更好的答案 - 问题出在我打开的 20 个选项卡之间,我对最佳实践感到困惑,推荐 ADT 而不是枚举的人数很多.而我并没有完全掌握。
猜你喜欢
  • 2017-10-17
  • 2023-03-11
  • 2019-01-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-13
  • 1970-01-01
相关资源
最近更新 更多