2011-07-31 15 views
6

Digamos que tengo dos características que me gustaría mezclar en una clase. Los rasgos implementan cada uno un método abstracto que la clase necesita.Rasgos y serialización/deserialización

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 continuación, puedo construir un hormigón M con A o B y serializar:

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 

Todo funciona como se esperaba. Pero, ¿cómo deserializo los objetos respetando el tipo de rasgo que se mezcla en M? Podría mostrar el nombre del rasgo en el método de serialización y luego tener el envío del método de deserialización de M en el nombre, pero ¿qué sucede si tengo otras clases además de M en las que se pueden mezclar A y B? Entonces, cada clase tendría que duplicar el comportamiento de la deserialización del despacho de M. El problema empeora si tengo varios rasgos que necesitan mezclarse en un objeto para hacerlo concreto y cada uno tiene su propia serialización/deserialización personalizada para hacer. ¿Alguien aborda un problema como este?

Respuesta

5

Sí, la gente tiene. El camino a seguir es utilizar el patrón de tipo de letra defendido por David MacIver sbinary y Debasish Ghosh sjson. trilogía de Debasish

son especialmente útiles para todos los programadores Scala intermedios.

Hoy en día, muchas de las bibliotecas están adoptando esta metodología, incluida la mía scalaxb. Veo

que he tomado la idea de nombramiento de Scala Colecciones CanBuildFrom y nombrado mis clases de tipos de la siguiente manera:

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] 

Editar:

¿Podría explicarme cómo el marco elige entre "con A" o "con B"?

Utilizando el patrón de clase de clase, las bibliotecas no se mezclan ni en A ni B. Tomar scalaxb por ejemplo, se proporciona un método llamado scalaxb.fromXML en el objeto de paquete definido de la siguiente manera:

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

Teniendo en cuenta que usted tiene documento XML, y que desea deserializar (deserializar) a ipo.Address objeto, lo haría llamar

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

el objeto Address permanece pura usando el patrón de clase de tipos:

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

¿Cómo sabe el compilador qué hacer?La magia es el parámetro implícito requerido por fromXML llamado implicit format: XMLFormat[A]. Esto requiere que tenga XMLFormat[Address] disponible como un valor implícito dentro del alcance donde se llama al scalaxb.fromXML[ipo.Address](...).

Esto está disponible en el código generado por scalaxb porque se mezcla en XMLProtocol en el paquete objeto del paquete ipo. Y ipo.XMLProtocol define

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

Edit2:

Creo que estoy empezando a entender la pregunta real. Usted tiene un objeto que consiste en rasgos mixins, y desea de alguna manera "deserializar" la composición de rasgos en otro proceso. Mientras escribía, podría incluir alguna etiqueta para cada característica y cargar lo que pueda.

Como he escrito hasta ahora sobre el patrón de tipo de letra, permítanme continuar con el enfoque. Lo bueno de tener el código de serialización fuera del del objeto es que podrías describir la combinación de mezcla del objeto. Supongamos que hay rasgos

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

y que desea describir el mixin como <obj><foo>1</foo><bar>2</bar></obj>. Aquí hay un gist que aumenté. I definidos clase de tipos de instancia para Foo, Bar y Foo with Bar, y llamó

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

que volvió

Right(FooWithBar(1, 2)) 
+0

No es claro cómo estas bibliotecas eligen qué rasgo de mixin a la clase de deserialización. ¿Podría explicarme cómo el marco elige entre "con A" o "con B"? – AnthonyF

+0

Así que el rasgo A y el rasgo B contienen diferentes implementaciones de un método abstracto en la clase M. He editado la pregunta para reflejar ese hecho. Estoy buscando componer un objeto con comportamiento definido en rasgos y permitir que ese objeto sea serializado y deserializado. En otras palabras, la instancia M concreta no es solo una clase de datos. Tiene un comportamiento específico. – AnthonyF

+0

Con deserialization/umarshaling, debe colocar la implementación fuera de la clase porque el objeto aún no existe. He demostrado una forma de implementar esto usando typeclass, pero también puedes hacer esto usando un objeto complementario si quieres. –