2011-07-31 13 views
6

Powiedz, że mam dwie cechy, które chciałbym wymieszać z klasą. Każdy z nich stosuje abstrakcyjną metodę, której potrzebuje klasa.Cechy i serializacja/deserializacja

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 
} 

można następnie skonstruować betonu M z A lub B i serializacji:

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 

wszystko działa prawidłowo. Ale w jaki sposób deserializować obiekty przy jednoczesnym poszanowaniu typu cechy, która jest mieszana z M? Mógłbym wypisać nazwę cechy w metodzie serializacji, a następnie M odserializował wysłanie metody na nazwę, ale co, jeśli mam klasy inne niż M, w które można mieszać A i B? Następnie każda klasa musiałaby duplikować zachowanie deserializatora M wysyłania. Problem staje się jeszcze gorszy, jeśli mam wiele cech, które muszą zostać zmiksowane w obiekt, aby był konkretny i każdy ma własną niestandardową serializację/deserializację. Ktoś rozwiązuje taki problem?

Odpowiedz

5

Tak, ludzie mają. Najlepszym sposobem jest użycie wzoru typeclassa, który jest wspierany przez Davida MacIvera: sbinary i Debasish Ghosh's sjson. Debasish za trylogia

są szczególnie przydatne dla wszystkich pośrednich programistów Scala.

Obecnie wiele bibliotek przyjmuje tę metodologię, w tym moją kopalnię scalaxb. Zobacz

Mam pomysł zapożyczony od nazewnictwa Scala Kolekcje CanBuildFrom i nazwał moje typeclasses następująco:

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] 

Edycja:

Czy możesz mi wyjaśnić, jak framework wybiera między "z A" lub "z B"?

Biblioteki nie zawierają ani A ani B. Aby wziąć scalaxb na przykład, zapewnia metodę o nazwie scalaxb.fromXML w obiekcie pakiet zdefiniowane następująco:

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) 
} 

Biorąc pod uwagę, że masz dokumentu XML, a chcesz unmarshal (deserializowania) go ipo.Address obiektu, byś zadzwoń

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

przedmiotem Address pozostaje czysta przy użyciu wzorca typeclass:

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

Jak kompilator wie, co robić?Magia jest niejawnym parametrem wymaganym przez fromXML o nazwie implicit format: XMLFormat[A]. Wymaga to dostępności XMLFormat[Address] jako niejawnej wartości w zakresie, w którym jest wywoływana scalaxb.fromXML[ipo.Address](...).

Udostępniono to w kodzie wygenerowanym przez program scalaxb, ponieważ miksuje on w obiekcie pakietu ipo do pakietu. I ipo.XMLProtocol definiuje

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

Edit2:

Chyba zaczynam rozumieć rzeczywiste pytanie. Masz obiekt składający się z mieszanki cech i chcesz jakoś "deserializować" kompozycję cechy w innym procesie. Jak napisałeś, możesz dodać tag dla każdej cechy i załadować wszystko, co możesz.

Skoro pisałem do tej pory na wzór typograficzny, pozwól mi kontynuować podejście. Sprawą czystą jest to, że kod serializacji poza obiektem obiektu jest w stanie opisać kombinację tego obiektu. Załóżmy, że istnieją cechy

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

i chcesz opisać wstawek jak <obj><foo>1</foo><bar>2</bar></obj>. Oto gist Wziąłem bat. zdefiniowałem typeclass instancję dla Foo, Bar i Foo with Bar i nazwał

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

który zwrócony

Right(FooWithBar(1, 2)) 
+0

To nie jest dla mnie jasne, w jaki sposób te biblioteki wybrać cecha do wstawek do klasy na deserializacji. Czy możesz mi wyjaśnić, w jaki sposób framework wybiera pomiędzy "z A" lub "z B"? – AnthonyF

+0

Zatem cecha A i cecha B zawierają różne implementacje metody abstrakcyjnej w klasie M. Zmieniłem to pytanie, aby odzwierciedlić ten fakt. Szukam komponowania obiektu z zachowaniem zdefiniowanym w cechach i umożliwienia serializacji i deserializacji tego obiektu. Innymi słowy konkretna instancja M to nie tylko klasa danych. Ma określone zachowanie. – AnthonyF

+0

W przypadku deserializacji/umarshaling należy umieścić implementację poza klasą, ponieważ obiekt jeszcze nie istnieje. Pokazałem sposób implementacji tego poprzez użycie typeclass, ale można to również zrobić za pomocą obiektu towarzyszącego, jeśli chcesz. –