压倒性的& Scala中继承的辅助构造函数

时间:2013-06-12 18:04:49

标签: scala inheritance constructor coding-style

假设我想在Scala中表示Book,它是直接从XML生成的。 我想要一个包装父类XMLObject来包含可以直接映射到XML的类。

下面是一个有效的实现示例,但我想知道为什么构造函数不能是抽象的,你不能使用 override 关键字,但你仍然可以重新定义一个构造函数,与子类中的父元素具有相同的签名,并使其按预期方式工作。

这被认为是“糟糕的”编码实践,如果是这样,那么获得类似功能的更好方法是什么?

abstract class XMLObject {
  def toXML:Node
  def this(xml:Node) = this()
}

class Book(

  val author:String = "",
  val title:String = "",
  val genre:String = "",
  val price:Double = 0,
  val publishDate:Date = null,
  val description:String = "",
  val id:Int = 0

  ) extends XMLObject {

  override def toXML:Node =
    <book id="{id}">
      ...
    </book>

  def this(xml:Node) = {
    this(
      author = (xml \ "author").text,
      title = (xml \ "title").text,
      genre = (xml \ "genre").text,
      price = (xml \ "price").text.toDouble,
      publishDate = (new SimpleDateFormat("yyyy-MM-dd")).parse((xml \ "publish_date").text),
      description = (xml \ "description").text
    )
  }
}

使用示例:

val book = new Book(someXMLNode)

2 个答案:

答案 0 :(得分:1)

只能以以下形式调用构造函数:

new X(...)

这意味着您知道要创建的对象的运行时类型。意义重写在这里毫无意义。您仍然可以在抽象类中定义构造函数,但这是用于链接(在类构造函数中调用超类构造函数)。

你似乎在寻找的是一种工厂模式:

  1. XMLObject
  2. 中删除构造函数
  3. 如果需要,可以向XMLObject的伴侣添加一个函数,该函数根据您传入的XML决定,创建哪个子类。
  4. 例如:

    object XMLObject {
      def apply(xml: Node) = xml match {
        case <book> _ </book> => new Book(xml)
        // ...
        case _ => sys.error("malformed element")
      }
    }
    

答案 1 :(得分:1)

我会为此使用类型。

您希望能够将书籍(和其他内容)映射到XML和从XML映射的事实与 的实体正交。您不希望仅基于您希望这些对象具有一些共同的XML映射功能这一事实来选择类层次结构。 Book的正确超类可能是PublishedEntity或类似的东西,但不是XMLObject。

如果你想在下周添加JSON解析/渲染会怎么样?您已经将超类用于XML;你会做什么?

更好的解决方案是为XML接口创建一个特性并将其混合在一起。通过这种方式,您可以根据需要混合使用尽可能多的内容,并且仍然可以自由选择合理的类层次结构。

但更好的解决方案是类型类,因为它们允许您添加对其他人类的支持,您无法添加方法。

这是a slide presentation that Erik Osheim prepared on type classes

许多JSON解析器/格式化程序包(例如Spray)使用类型类。我没有在Scala中使用过多的XML,但我猜也有XML的类型类实现。快速搜索this