14/10/2008

It won't work, if you don't use the right objects

I had a very interesting problem yesterday. I am slowly moving forward on building the threaded discussion system. I have worked out how to render the basic HTML, as a series of components, and I was at the stage of wanting to render the discussion items. My question was how to represent them?

In an earlier post I had looked at how one might identify the objects in a system, and in particular which ones should be written from scratch. The basic idea was that if one were storing information that already 'fitted' into a class, there was no reason to create something new. The class could be a simple one, such as Array, or something more complex such as Person. In either case, the basic principle was - don't reinvent the wheel. However, as we will see sometimes reinventing the wheel is the right thing to do!

I decided to represent the conversation as an array of nested arrays, e.g.,

discussion := #(topic1 topic2 (sub-topic2.1 sub-topic2.2) topic3).

Initially, the structure seemed to work well. It was simple, easy to traverse and didn't involve the creation of any new classes. I was quite pleased with myself for following the design principles. Unfortunately, I soon discovered that rendering the subarrays was a pain. Basically, I want Seaside to create an Ordered List and add the entries as List Items. When it comes across a subarray, it should start another ol tag and add the elements.

I played with this problem for a little while, but everything I did seemed ugly, and not very smalltalk. In the end, I asked on the beginners' list and received two interesting replies:

  1. Randal suggested using the Visitor Pattern

  2. Ramon suggested creating two new objects and using recursion (see below)


I had never heard of the visitor pattern, but fortunately Wikipedia came to the rescue. AIUI this would involve adding a method to both the array, and the strings/arrays it contains, so that they know how to render themselves. Ramon's code, more directly challenged the way I was storing the discussion threads. He suggested something like this:

renderPost: aPost on: html
html render: aPost.
aPost comments do:
[ :each | self renderComment: each on: html ]

renderComment: aComment on: html
html html: aComment body.
aComment comments do:
[ :each | self renderComment: each on: html ]

For the time being, I am going to try Ramon's approach, purely because I have a better understanding of how to do it. However, I am now intrigued by the visitor pattern, so I will go back to that, once I understand a bit more about OO design. However, for me, the most interesting aspect of this whole problem was the question of why couldn't I see the answer? I think the explanation is that once I had decided to go with an Array, my thinking was limited to that set of methods etc. And, the choice of the Array was driven by the fact that the data could be made to fit into it. So, I now need to develop a more sophisticated design heuristic when thinking about object modelling. Basically, just because something could fit, doesn't mean it should. Now, obviously, design is an iterative process, and all programmers run around refactoring, sooner or later. What I am trying to understand is how I might think about the problem in a more flexible manner.

1 comment:

intrader said...

I think that this post is one of the best about the design process. One problem I grappled early on was the the abyss between procedural programming and OOP. Well, I think that this happens also in the world of data structures and their represetation.