From Unit Test to (Slightly) Odd Design

Published 27 June 07 05:35 AM | andersnoras 

"And you may find yourself in a beautiful house, with a beautiful wife,
And you may ask yourself - how did I get here?"
Talking Heads - Once in a Lifetime

Recently I needed to write a library that produces various iCalendar components such as VEvents and VTodos. The model is quite simple, each component can have a number of different properties and each property can have a number of parameters. There are different rules for how the properties can be used with the different components, but the schema is consistent for all components. For instance a VEvent cannot have both a DTEND and a DURATION property at the same time, but you can have zero or one CLASS or DESCRIPTION properties and any number of COMMENT properties. Similar rules apply to which parameters a property can have.
For the model as a whole there are few components and a large amount of properties and parameters.

As always; I wrote the unit tests first and refactored my way to an extensible design. The only thing that puzzles me is the design I ended up with.
The model is a perfect match for a OO-model. Below is a class diagram depicting some of the types in my final design.

image

At the bottom you can find the V* components, which inherit from the abstract Component class. Each Component has a collection of properties. The Property class is abstract, but you can see a feq concrete properties at the top. Each property has a collection of parameters. Again the Parameter class is abstract, and there are some concrete implementations at the right hand side of the model.
You cannot really see anything odd on this 10,000 foot view, but if we peek inside some of the classes things turned out rather strange.

[OneOrLess("DTSTART","GEO","LOCATION","ORGANIZER","STATUS","SUMMARY","TRANSP","URL","RECUR")]
[One("DTEND","DURATION")]
[ZeroOrMore("ATTENDEE","CONTACT")]
public class VEvent : Component
{
    public VEvent(params Property[] properties) : base(properties)
    {
    }
}

The rules for which property usage is totally declarative and the rules are enforced in the Component base class. The same design is used for the properties.

[OneOrLess("ALTREP","LANGUAGE")]
[ZeroOrMore("XPARAM")]
public class Location : TextProperty
{
    private Uri locationUri;

    public Location(string location) : base(location)
    {
    }

    public Location(string location, Uri locationUri) : base(location)
    {
        this.locationUri = locationUri;
    }

    public override string Value
    {
        get
        {
            if (locationUri != null)
            {
                return string.Format("\"{0}\":{1}", locationUri, base.Value);
            }
            else
            {
                return base.Value;
            }
        }
    }
}

No matter how odd looking the design is, the semi-dynamic design pleasant to work with and new parameters, properties and components can be added in a matter of seconds. Still I have to ask myself; How did I get here?

Filed under: ,

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

# Jonas said on June 29, 2007 11:17 PM:

Anders,

I usually enjoy your post's and insights but ... YAGNI.

And I'm not talking about the code Im talking about the diagram.... Please ;)

Thanks

/Jonas

# andersnoras said on July 2, 2007 4:43 AM:

@Jonas;

I'm not sure if I follow you here Jonas. The diagram is just another way to view the code. I reverse engineered it off the source code.

# anonymous said on July 6, 2007 2:43 PM:

the diagram is blurry -- what are Property and Parameter inheriting from?

# andersnoras said on July 9, 2007 12:54 AM:

@anonymous;

The Property and Parameter classes extend an abstract class called NameValueElment:

public abstract class NameValueElement

{

   public virtual string Name

   {

       get { return GetType().Name.ToUpperInvariant(); }

   }

   public abstract string Value { get; }

}

Anyone reading this might want to know that I've revisited the design of the framework after posting this. The design detailed in this post is the results of the red -> green phase of TDD, I'll post a follow up on the green -> refactor phase later.

Leave a Comment

(required) 
(optional)
(required) 
Enter the code you see below