1

Context: design with correlated polymorphic classes

For example, let's consider the following class definitions for representing messages that we would get from some remote service:

class ImageMessage : IMediaMessage
class VideoMessage : IMediaMessage
class DocumentMessage : IMediaMessage

The messages would arrive in a high level encoding such as for example JSON and it looks like this:

{
 type: 'image|video|document',
 ...
}

Then depending on the type, we would use a factory to create an instance of the message of the right concrete type.

Now, we're writing a web application and need to display the messages too. The client of the code just knows it is working with a Collection<IMediaMessage> and needs to be able to delegate to specific views, for example:

class ImageView : IMediaView
class VideoView : IMediaView
class DocumentView : IMediaView

So we end up needing to right yet "another piece of code" to dispatch the message types to their corresponding views. In Javascript this might be done with a switch and instanceof checks since the "compiler" (there is not one) cannot pick the right one at runtime.

Problem: maintaining consistency when adding new concrete types

So, in this example, when adding a new media message type, the developer would have to remember to do two things:

  1. Update the factory for the new media message type, so it can be serialized to a new concrete type (and make this concrete type)
  2. Update and create a view to bind the new media message for display

Depending on what else happens, there many be other things that switch on but this is a pretty typical case.

How could the situation be improved ?

The questions then is:

  1. How do you communicate clearly to a new programmer that this is the process to add a new media message? Package ordering? Documentation?
  2. Is there a way to redesign this better so that it's more obvious without documentation?

    I was thinking of something like:

    createConcreteAndViewFor(mediaMessage) {
       return {
         view: ImageView,
         concrete: ImageMessage
       }
    }
    

    but this then fuses two concepts together, since it is possible to use the concrete type for other things, for example perhaps a generic

  3. Automated tests -- can they help? In these examples, scanning a) the type(s) extending and b) the types available in the media message type enum, one could assert that it is implemented at least once in each of the relevant classes... have the function throw if it cannot handle a new input that was created.

Important remark: This question is about how to solve dispatching and maintaining structures of polymorphic code in dynamic languages -- not about high level overview of dynamic languages!

5

3 Answers 3

2

I would define two interfaces on the inbound message:

class ImageMessage : IMediaMessage, IViewable
class VideoMessage : IMediaMessage, IViewable
class DocumentMessage : IMediaMessage, IViewable

The IMediaMessage is the interface that is needed to reconstruct the binary large object. The IViewable interface is the one that is needed to extract the metadata from the message.

Then define your view like this:

class MessageView<T> where T : IViewable

MessageView should be written where it can consume any object as long as it implements IViewable. If you are thoughtful about the IViewable interface and the view can be truly generic, this design should never require any if (typeof(T) == typeof(concreteType) and should not require modification as media types are added.

You will still need a factory to extract the messages from the JSON, but once the message is extracted, it ought to have enough data in it to serve both purposes.

0

For each new message type you need to make appropriate sub-classes of IMediaMessage and IMediaView and also teach the factory how to instantiate them. You cannot get away from this.

When a new message arrives, its type (image|video|document) needs to be read and the corresponding IMediaMessage and IMediaView object instantiated (possibly singletons).

If you have a Collection of IMediaMessage why can't each IMediaMessage object contain a reference to its matching IMediaView object? Then we don't "end up needing to right yet "another piece of code" to dispatch the message types to their corresponding views" ... because the client would dispatch polymorphically assuming all IMediaView subclasses implement the same interface.

I also struggled to understand this statement....

"In Javascript this might be done with a switch and instanceof checks since the "compiler" (there is not one) cannot pick the right one at runtime." Did you mean cannot pick the right one "at compile time"?. In general though, single dispatch in any language can be implemented by run-time code that examines types or by other means that require no runtime code (eg C++ vtable)- but why does this matter to your question?

1
  • He was referring to the limitations of weak typing. Commented Aug 23, 2018 at 0:57
-1

You only have some choices. Each message type has two data types that are tightly related, and the view I imagine is dependent on the message, but not vice-versa. Either:

  • Combine the two data types, so that one is nested in the other, and the factory for one is an instance of the other (probably not what you want).
  • Put the meta type (class) of each into a table or dictionary, where the key to the table is the string form of the message identifier, and the classes of each type are two values in the table, for that key.

In the second option the factory for either the message or view instance would probably need reflection, or use cloning (if storing an instance and not a class). The only advantage of the second approach is that the next developer will see that each table entry needs to support the creation of two data types.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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