2

I have many (at the moment around 30) different message classes in an application I am creating. Each of these messages need to be serialized and deserialized. However, the process of serializing and deserializing is mostly unique to each class (the format of the serialized message is specified in a document, and says which bits in the message goes where), but some are pretty straight forward and use the same process.

I thought about having each of them implement an interface, with Serialize() and Deserialize() methods. However, it seems cumbersome to implement this for each message, especially when many of the messages do in fact use the same code to do this serializing and deserializing.

I thought about having a class that contains a Dictionary<T, Func<IMessage, byte[]>, and a function to map a function to serialize to a specific type. Then use this dictionary to get the appropriate serializer.

Any thoughts? How would you go about this problem?

  • 1
    Define utility methods in the superclass for things that are done exactly the same by several message types, and override the serialization method by delegating to one of them. – Kilian Foth Apr 14 '15 at 13:21
  • 1
    In general, if you have a bunch of classes that need similar functionality, it's a sign that your class design is poor. That big chunk of similar functionality should be a class, and the little differentiation should be the parameterization. – Telastyn Apr 14 '15 at 13:40
  • @Telastyn Would you say that inheriting from a base class that contains the default functionality, and overriding it in special cases, would be a bad approach? To me it seems like it would work OK, but I don't think that the task of serialization and deserialization belongs to these message classes. – Walkingsteak Apr 14 '15 at 14:59
4

Sounds like you can cover this using straight-forward inheritance. Create a base class with the common implementation, and create subclasses that override that implementation with their own, unique behaviour.

You can read up on MSDN here.

EDIT: I figured I should elaborate with an example. Define a base class that implements the default behavior of your (de)serialization.

abstract class Message {
   public virtual void Serialize() {
      // Default logic here
   }

   public virtual void Deserialize() {
      // Default logic here
   }
}

Your derived class can then override these methods and specifiy their own behaviour if necessary.

class SpecialMessage : Message {
   public override void Serialize() {
      // Specific logic here
   }

   public override void Deserialize() {
      // Specific logic here
   }
}

Any classes that inherit from the Message class that do not override these methods, will use the default methods defined in the base class.

1

Generic answer: My gut feeling says if you have 30 different but very similar things, you should go one metalevel up, have one class for all of them and push the difference completely down to the data. Even if you end up with a nano VM / nano interpreter. Maybe during implementing it, you find out you really only have two or three kinds of them, parametrizable by different kind of data.

0

I would recommend putting the serialization and deserialization code in separate classes rather than the data model classes like it sounds like you are thinking.

I'm solving a similar problem now and I have three distinct groups of classes. The first is the model for the messages that contain the various message fields. There's no validation or logic in them, they are really DTOs. All of the messages inherit from a marker interface.

The next group of classes is the serialization and deserialization class. I have two interfaces - one for serialization and one for deserialization. They each have one method that accepts a Message and returns a generic time (for serialization) and one method that returns a Message and accepts a generic type. I implement this interface in serializers and deserializers for XML Strings (the generic is of type String), binary (the generic is of type byte[]), and Properties (the generic is of type Properties).

The final group of classes is my validators. These are more of a logical grouping that ensure the messages are in accordance with the spec. They return List of validation issues, returning an empty List if validation is successful.

The advantage of this approach is that it completely decouples your message data from your message rules from your transformation processes and makes it trivial to establish new sets of rules or serialization/deserialization formats.

  • 2
    What's the point of the marker interfaces? In my experience, they only lead to badness in popular languages. – Telastyn Apr 14 '15 at 13:45
  • @Telastyn In my implementation, I'm dealing with commands and responses. There are instances where I need a way to group them together and accept them into a common method or into a common data structure. This allows me to do that. Aside from being messages, commands and responses have no shared attributes or operations. – Thomas Owens Apr 14 '15 at 13:47
  • @ThomasOwens A tagged union would let you do that without having to modify the existing classes and without the possibility of anyone sneaking in unexpected types. Although it takes some boilerplate to make one in Java/C#, it can be done. It's basically a Visitor. A good abstraction doesn't need to be modified based on who's using it or how it's being used, so any design that forces a useless interface is a non-starter to me. – Doval Apr 14 '15 at 14:01
  • @ThomasOwens - right, but then you have to cast out of the marker in the common method/common data structure? – Telastyn Apr 14 '15 at 14:06
  • @Telastyn Yes. But at least it's not exposed to the client. There are better ways to do it in other languages, but being constrained to Java 6 limits my options. You may not need the marker interface at all. It all depends on what your data model looks like. – Thomas Owens Apr 14 '15 at 14:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.