Statically Typed

because Hindley-Milner rocks

XML, object creation and D-R-Y


I recently had the misfortune a few weeks back of having to add functionality to C++ code written by a novice developer.  How could I tell this developer was a novice?  First clue:  everything implemented the singleton pattern unless it publicly exposed all it’s fields as statics.  Second clue:  all the methods were several pages in length.  Final clue:  D-R-Y was completely ignored.  Miraculously the project worked so long as no corner cases were hit, then it crashed and burned.

The design concepts weren’t totally out of whack and showed some promise.  The developer had taken the time to think about encapsulated some ideas into concrete classes.  For instance, there were a collection of XML documents which acted as configuration files.  For each of these XML documents a single class was instantiated (remember, singletons.)  Those classes would drive different behavior and make the code appear from the outside quite fluid.

We’ve all been there before; complete novices, fresh out of school, ready to tackle the world.  I remember the first commercial grade code I wrote.  It was hideous but I was damned proud of my creation.  Fast forward to today and there’s a different story; hardened developer, longing for those old school days, ready to decapitate the next sales guy that promises a customer the world.

So I thought I’d take the time to do a little show and tell.  Obviously this isn’t a copy and paste.  Everyone has feelings and their pride.  I have mine and I know what it’s like to have them hurt.  First thing is first, how to approach reading an XML file.

There really are two choices when handling XML, SAX and DOM.  I’ve used both and both have good merits.  For this code the author chose to use the tinyXML library (DOM style parsing.)  Let’s consider some class with the following public implementation:

class Dinner {
public:
    Dinner();

    void SetFork(double _input);
    void SetSpoon(double _input);
    void SetKnife(double _input);
    void SetPlate(double _input);
    void SetGlass(double _input);
};

How would you go about implementing the reading functionality?  We have several choices:

  • Dinner ParseDinnerXml(string _name);
  • Dinner ParseDinnerXml(TiXmlDocument _doc);
  • Dinner ParseDinner(TiXmlElement *_root);

The first takes in a name, opens the xml, reads the xml, assigns values to the Dinner object and passes it out.  That’s alot and it’s what the original author did.  The second is an improvement which assumes an xml document is passed in and reads from there but it is less flexible than the third.  The third allows for future modification in case all the XML documents get combined into one so some other developer doesn’t have to spend half a day finding all the various documents just to get a program to work.  It only needs the element which contains the Dinner information.

Then how might the code look?  If you said this:

Dinner ParseDinner(TiXmlElement *_root) {
    Dinner output;
    TiXmlElement *child = _root->NextSiblingElement("Fork");
    if( child != NULL )
        if( child->GetText() != NULL )
            output.SetFork( atof( child->GetText() ) );

    TiXmlElement *child2 = child->NextSiblingElement("Spoon");
    if( child2 != NULL )
        if( child2->GetText() != NULL )
            output.SetSpoon( atof( child2->GetText() ) );

    child = child2->NextSiblingElement("Knife");
    if( child != NULL )
        if( child->GetText() != NULL )
            output.SetKnife( atof( child->GetText() ) );

    child2 = child->NextSinglingElement("Plate");
    if( child2 != NULL )
        if( child2->GetText() != NULL )
            output.SetPlate( atof( child2->GetText() ) );

    child = child2->NextSiblingElement("Glass");
    if( child != NULL )
        if( child->GetText() != NULL )
            output.SetGlass( atof( child->GetText() ) );

    return output; }

I’d have to hurt you.  Just kidding.  What I encountered was worse but I made it better.  How?  Like this:

Dinner ParseDinner(TiXmlElement *_root) {
    Dinner output;

    Assign( _root->FirstChildElement("Fork"), output, &Dinner::SetFork);
    Assign( _root->FirstChildElement("Spoon"), output, &Dinner::SetSpoon);
    Assign( _root->FirstChildElement("Knife"), output, &Dinner::SetKnife);
    Assign( _root->FirstChildElement("Plate"), output, &Dinner::SetPlate);
    Assign( _root->FirstChildElement("Glass"), output, &Dinner::SetGlass);

    return output; }

template<typename T, typename F>
void Assign(TiXmlElement *_elem, T &_object, F _func) {
    if( _elem != NULL && _elem->GetText() != NULL ) {
        (_object.*_func)( atof( _elem->GetText() ) ); }
}

This might at first glance not look like much of an improvement other than freeing the order of the XML tags but imagine if those pesky XML documents contained 50 tags each.  Sure, I could just write it all with a simple Python script (which I did, to make this entry) but this way doesn’t violate D-R-Y.  The line count is down, the chance for error reduced, and the functional traceability is clear.

Advertisements

2 comments on “XML, object creation and D-R-Y

  1. Roger Pate
    March 8, 2010

    Misplaced paren in the last code snippet, should be “(_object.*_func)”.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Information

This entry was posted on February 18, 2010 by in C++.
%d bloggers like this: