views:

206

answers:

3

I have a calendar event object. I have plans to make it compatible with CalDAV/iCal/vCal protocols/file formats, which require the event be serialized and de-serialized to and from different formats.

I could write an ImportICal, ExportICal, ImportVCal, ExportVCal, etc. set of methods, but that doesn't seem like a very good approach, because what if the vCal format is updated, etc.

Has anyone dealt with this type of import/export situation before? If so, what design pattern (if any) is generally best?

Thanks for your help!

A: 

If the vCal format is updated, you will have to change whatever code you have written whichever design pattern you use (unless they decide to switch to something like ASN.1 where upgrades are baked in).

I would create a format interface with import and export methods, and possibly metadata and methods for testing whether a random bit of XML is likely to be that format. Then for each different format, you have an object which implements that interface. This is sort of a 'strategy design pattern', but each format represents several strategies for doing a cohesive set of things (import,export,detection) rather than having separate strategy objects.

Pete Kirkham
+4  A: 

I am not particulary familiar with those formats but I'd create an simple data transfer object that represents your genereric calendar event object. It does nothing but holding the data (pseudocode):

class CalendarEvent
{
    DateTime Date { get; }
    string Title { get; }
    string Description { get; }
}

Then you create an interface for CalendarEventReader and CalendarEventWriter (it's Strategy pattern and maybe the Builder pattern, sort of):

interface ICalendarEventReader
{
     CalendarEvent Read(Stream data);
     // Add additional methods if needed e.g.:
     string GetTitleOnly(Stream data);
}
interface ICalendarEventWriter
{
     Stream Write(CalendarEvent event);
     // Add additional methods if needed e.g.:
     Stream WriteSummaryOnly(CalendarEvent event);
}

Then have actual implementations implement the above interfaces. One for each format. You can even think about having reader and writer in the same class:

class CalDavConverter : ICalenderEventWriter, ICalendarEventReader
{
    ...
}

You'd then have a Repository (it's the Factory pattern maybe with Singleton) that maintains a list of ICalenderEventReader/Writer implementations for the different formats:

static class CalenderEventConverterRepository
{
    static ICalendarEventReader GetReader(string formatName /*or any other data upon wich to decide wich format is needed*/)
    { 
    ...
    }

    static ICalendarEventReader GetWriter(string formatName /*or any other data upon wich to decide wich format is needed*/)
    { 
    ...
    }
}
bitbonk
I don't think the reader writer interfaces can be called a builder. But apart from that, +1 for a good design suggestion.
Tanmay
The solution I came up with on my own was similar (minus the factory part). What would the client code look like then? Would the calendar object use that factory, or would the client code use it?
Brandon Montgomery
A: 

Usual way to arrange multiple implementations (calendar protocols in your case) with a single common interface is Bridge Pattern.

Vanya
This would mean I would pass an implementation of, say, ICalExporter to the constructor of the CalendarEvent object. Then, on the CalendarEvent object, I call Export() and it uses that ICalExporter object to export the event to whatever format. What if I need it in both formats?
Brandon Montgomery