Tuesday 2 February 2010

Re-factoring FTW!

Recently I decided to take a look over my project (call it a code review) and I came to the conclusion it was time to have a bit of a re-write. I wasn't going to change a large amount of the underlying code itself (I'll leave the joy of optimisation for later) but what I decided to do was to restructure the design of my code so everything would fit together a bit more nicely, and the relationships between classes would make more sense. Another thing I decided to do was to re-name a large amount of methods and classes to give them better more coherent names/descriptors (no more methods called DoCoolStuff() I promise).

I first looked at classes individually and decided exactly what behaviours I wanted them to do. I moved behaviours present in one class to another if they seemed more appropriate in that context, and in other cases split methods in a class into several smaller ones with much more specific names and functionality.

Doing this immediately makes classes easier to deal with; it gives you the ability to work with your classes by just looking at their interfaces and not having to worry about the implementation details of each class.

Once this was done I  decided to begin re-factoring the relationships between the classes themselves. Here is a brief example of how my design changed over time as a result of this.

When I started working on my tower defence game, the code for say, drawing a tower, was right there in the tower.cpp file. This was immediately a bad idea, what I needed was to abstract the drawing routine out into a renderer class, that the tower would then have access to. I went about giving each tower its own renderer, but then realised this was bad, because it meant I was creating lots of renderers when I only really needed one. So I then took the renderer out of the individual classes and created one instance of renderer in my PlayingState class, and it was then passed into a tower draw call when a tower wanted to be drawn. The design was almost there, but today I brought the renderer up one more level so it actually became part of my Engine. I now pass a pointer to my engine to the PlayingState and it then in turn contains the call to the renderer itself.

Now (almost) all of my drawing code is held in this renderer class, and in order to draw anything (or make any OpenGL calls) you must use this class. This gives me a level of abstraction that allows me (in theory I hasten to add) the ability to 'unplug' my current renderer and attach an entirely new one using another graphics API (DirectX says what's up).

What I find interesting is this design evolved with how I was writing my code. I knew in the beginning that the idea of having the renderer as part of the engine was a good idea, I had been told about it, and had read about similar techniques, but I didn't really understand how to implement it from the get go. If you'll forgive the analogy I think I needed to paddle in the shallows before before diving into deeper waters, but what was interesting was this pattern emerged naturally.

So what started out as some (procrastinating) re-factoring, ended up as a pretty big re-write (well for me anyway) and the lessons I have taken away from it are absolutely invaluable, more so I think for having done it wrong the first time.

2 comments:

Unknown said...

Interesting read as always. I might go and read your back catalogue.

Thought I'd note another couple of errata :)

"them selves" should be "themselves"

"it's" should be "its" when using it in the possessive sense. Read/download "Eats, Shoots and Leaves". You'll find it useful :)

"more so" should be "moreso"

Lastly, the "eminating" in your blog slogan should be "emanating".

tomhhh said...

Thanks Lewis, you should become an editor. I would appreciate line numbers though if you'd be so kind ;)
Thanks for the comment though. Take it easy.