views:

111

answers:

4

The Facts

I have the following datastructure consisting of a table and a list of attributes (simplified):

class Table {
    List<Attribute> m_attributes;
}

abstract class Attribute {}

class LongAttribute extends Attribute {}
class StringAttribute extends Attribute {}
class DateAttribute extends Attribute {}
...

Now I want to do different actions with this datastructure:

  • print it in XML notation
  • print it in textual form
  • create an SQL insert statement
  • create an SQL update statement
  • initialize it from a SQL result set

First Try

My first attempt was to put all these functionality inside the Attribute, but then the Attribute was overloaded with very different responsibilities.

Alternative

It feels like a visitor pattern could do the job very well instead, but on the other side it looks like overkill for this simple structure.

Question

What's the most elegant way to solve this?

+1  A: 

The Command pattern comes to mind, or a small variation of it.

You have a bunch of classes, each of which is specialized to do a certain thing with your data class. You can keep these classes in a hashmap or some other structure where an external choice can pick one for execution. To do your thing, you call the selected Command's execute() method with your data as an argument.


Edit: Elaboration.

At the bottom level, you need to do something with each attribute of a data row. This indeed sounds like a case for the Visitor pattern: Visitor simulates a double dispatch operation, insofar as you are able to combine a variable "victim" object with a variable "operation" encapsulated in a method.

Your attributes all want to be xml-ed, text-ed, insert-ed updat-ed and initializ-ed. So you end up with a matrix of 5 x 3 classes to do each of these 5 operations to each of 3 attribute types. The rest of the machinery of the visitor pattern will traverse your list of attributes for you and apply the correct visitor for the operation you chose in the right way for each attribute.

Writing 15 classes plus interface(s) does sound a little heavy. You can do this and have a very general and flexible solution. On the other hand, in the time you've spent thinking about a solution, you could have hacked together the code to it for the currently known structure and crossed your fingers that the shape of your classes won't change too much too often.

Where I thought of the command pattern was for choosing among a variety of similar operations. If the operation to be performed came in as a String, perhaps in a script or configuration file or such, you could then have a mapping from

"xml" -> XmlifierCommand
"text" -> TextPrinterCommand
"serial" -> SerializerCommand

...where each of those Commands would then fire up the appropriate Visitor to do the job. But as the operation is more likely to be determined in code, you probably don't need this.

Carl Smotricz
I don't get it. How should the command pattern match to the given problem?
tangens
More detail added in my answer.
Carl Smotricz
Thanks for the details. I have to think about that...
tangens
+1  A: 

I would look at using a combination of JAXB and Hibernate.

JAXB will let you marshall and unmarshall from XML. By default, properties are converted to elements with the same name as the property, but that can be controlled via @XmlElement and @XmlAttribute annotations.

Hibernate (or JPA) are the standard ways of moving data objects to and from a database.

Nathan Voxland
For distant future this could be the way to go, but for now I just have to clean up my old structures.
tangens
Both are very flexible for working with existing structures. Configuration can be done either by annotations on the columns, or by XML config files which would allow you to add XML input/output and database support without touching your existing classes at all.
Nathan Voxland
+1  A: 
Marcin
Agreed, I'll use some ORM in the nearer future. But these attributes store much more information, e.g. some diplay hints for the GUI, online help, and so on. So I can't get rid of them even if I switch to ORM immediately.
tangens
A: 

In the end, I used the visitor pattern. Now looking back, it was a good choice.

tangens