2011-07-31 157 views
6

說我有兩個特性,我想混入一個類。每個特徵都實現了類需要的抽象方法。性狀和序列化/反序列化

trait Writable { 
    def serialize(out: java.io.DataOutput) 
} 

trait T1 extends Writable 

trait A extends T1 { 
    val aNum: Int 
    abstract override def serialize(out: java.io.DataOutput) = { 
     super.serialize(out) 
     println("A serialize") 
     out.writeInt(aNum) 
    } 

    def action = println("A action") 
} 

trait B extends T1 { 
    val bNum: Int 
    abstract override def serialize(out: java.io.DataOutput) = { 
     super.serialize(out) 
     println("B serialize") 
     out.writeInt(bNum) 
    } 

    def action = println("B action") 
} 

abstract class M[CT1 <: T1](val mNum: Int) extends Writable { 
    this: M[CT1] with T1 => 
    def serialize(out: java.io.DataOutput) = { 
     println("M serialize") 
     out.writeInt(mNum) 
    } 

    def action 
} 

我可以然後構造一個混凝土米,A或B和序列:

scala> val m1 = new M[A](10) with A { val aNum = 20 } 
m1: M[A] with A = [email protected] 

scala> val m2 = new M[B](20) with B { val bNum = 30 } 
m2: M[B] with B = [email protected] 

scala> val out = new java.io.DataOutputStream(new java.io.ByteArrayOutputStream()) 
out: java.io.DataOutputStream = [email protected] 

scala> m1.serialize(out) 
M serialize 
A serialize 

scala> m2.serialize(out) 
M serialize 
B serialize 

一切正常。但是,在尊重混入M中的特徵類型的同時如何反序列化這些對象呢?我可以在序列化方法中輸出特徵的名稱,然後在名稱上使用M的反序列化方法調度,但是如果我有除M以外的類A和B可以混合的類,該怎麼辦?然後,每個類將不得不重複M的調度反序列化的行爲。如果我有多個特徵需要混合到一個對象中以使其具體化,並且每個特徵都有自己的自定義序列化/反序列化,則問題會變得更加嚴重。任何人都可以解決這樣的問題?

回答

5

是的,人們有。要走的路線是使用David MacIver的sbinary和Debasish Ghosh的sjson支持的類型模式。 Debasish三部曲

對所有中間Scala的程序員特別有用。

今天,許多圖書館都採用這種方法,包括我的scalaxb。見

我已經借了Scala集合CanBuildFrom命名的想法,並命名我的類型類如下:

trait CanReadXML[A] { 
    def reads(seq: scala.xml.NodeSeq): Either[String, A] 
} 

trait CanWriteXML[A] { 
    def writes(obj: A, namespace: Option[String], elementLabel: Option[String], 
     scope: NamespaceBinding, typeAttribute: Boolean): NodeSeq 
} 

trait XMLFormat[A] extends CanWriteXML[A] with CanReadXML[A] 

編輯

你能向我解釋一下嗎?框架選擇「與A」還是「與B」?

使用類型類型模式,庫既不混合A也不混合B。 採取例如scalaxb,它提供了一個定義爲包裝對象調用scalaxb.fromXML方法如下:

def fromXML[A](seq: NodeSeq, stack: List[ElemName] = Nil) 
       (implicit format: XMLFormat[A]): A = format.reads(seq, stack) match { 
    case Right(a) => a 
    case Left(a) => throw new ParserFailure(a) 
} 

既然你有XML文檔,並且要解組(反序列化)到ipo.Address對象,你會調用

scalaxb.fromXML[ipo.Address](<shipTo xmlns="http://www.example.com/IPO"> 
    <name>Foo</name> 
    <street>1537 Paper Street</street> 
    <city>Wilmington</city> 
</shipTo>) 

Address對象保持純使用類型類圖案:

case class Address(name: String, street: String, city: String) 

編譯器如何知道該怎麼做?神奇的是fromXML所要求的隱含參數,稱爲implicit format: XMLFormat[A]。這要求您在調用scalaxb.fromXML[ipo.Address](...)的範圍內有XMLFormat[Address]作爲隱式值。

這使得在scalaxb生成的代碼中可用,因爲它將XMLProtocol混合到ipo包的包對象中。而ipo.XMLProtocol定義

implicit lazy val IpoAddressFormat: scalaxb.XMLFormat[ipo.Address] = new DefaultIpoAddressFormat {} 

EDIT2

我想我開始瞭解實際問題。你有一個由trait mixin組成的對象,並且你想以某種方式「反序列化」其他進程中的特徵組合。 正如你寫的,你可以爲每個特徵包含一些標籤,並加載任何你可以。

由於我已經寫了類型類模式,讓我繼續這種方法。序列化代碼對象外部的整潔事情是,您可以實際描述對象的混合組合。假設有特點

trait Foo { def foo: Int } 
trait Bar { def bar: Int } 

,並要描述的mixin作爲<obj><foo>1</foo><bar>2</bar></obj>。 這是一個gist我鞭打。 我定義的類型類的實例FooBar,並且Foo with Bar,並呼籲

Def.fromXML[Foo with Bar](<obj><foo>1</foo><bar>2</bar></obj>) 

其返回

Right(FooWithBar(1, 2)) 
+0

這是我不清楚這些庫如何選擇哪個性狀混入到反序列化的類。您能否向我解釋框架如何在「與A」或「與B」之間選擇? – AnthonyF

+0

因此特質A和特質B包含M類中抽象方法的不同實現。我編輯了這個問題以反映這一事實。我正在尋找組成具有特性中定義的行爲的對象,並允許對該對象進行序列化和反序列化。換句話說,具體的M實例不僅僅是一個數據類。它有特定的行爲。 – AnthonyF

+0

隨着反序列化/取消編組,你需要將實現放在類之外,因爲對象還不存在。我已經通過使用typeclass演示了一種實現方法,但如果您願意,也可以使用伴隨對象來實現此目的。 –