假设我想在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)
答案 0 :(得分:1)
只能以以下形式调用构造函数:
new X(...)
这意味着您知道要创建的对象的运行时类型。意义重写在这里毫无意义。您仍然可以在抽象类中定义构造函数,但这是用于链接(在类构造函数中调用超类构造函数)。
你似乎在寻找的是一种工厂模式:
XMLObject
XMLObject
的伴侣添加一个函数,该函数根据您传入的XML决定,创建哪个子类。例如:
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。