Showing posts with label refactoring. Show all posts
Showing posts with label refactoring. Show all posts

Thursday, September 21, 2006

Duplication smells

Duplication. A programmer's worst nightmare. Does that sound over the top? Maybe it does, but I guess most programmers would agree that duplication is one of the worst code smells around. So, a programmer should do everything to prevent and reduce duplication, right? Well, I think that in the case of simple duplication we all agree on that. With simple I mean that two pieces of code are textually equal or very similar, e.g.:

patterns.Publisher().registerObserver(self.onMatchAllChanged,
eventType='view.taskcategoryfiltermatchall')
patterns.Publisher().registerObserver(self.onFilteredCategoriesChanged,
eventType='view.taskcategoryfilterlist')
patterns.Publisher().registerObserver(self.onAddCategoryToTask,
eventType='task.category.add')
patterns.Publisher().registerObserver(self.onRemoveCategoryFromTask,
eventType='task.category.remove')


This is actual code from Task Coach (blush) and represents a component registering itself as observer for different event types. Of course, this type of duplication is rather easy to fix, e.g.:

callbacks = {'view.taskcategoryfiltermatchall': self.onMatchAllChanged, ...}
publisher = patterns.Publisher()
for eventType, callback in callbacks.items():
publisher.registerObserver(callback, eventType)

But then there's also duplication that's not strictly textual, but more structural. For example, two for-loops looping over the same datastructure but with a different body. An example from Task Coach again. These two event handlers update a check-listbox (a list with items that can be checked and unchecked by the user) when categories are added to or removed from a task:

def onAddCategoryToTask(self, event):
for category in event.values():
if self._checkListBox.FindString(category) == wx.NOT_FOUND:
self._checkListBox.Append(category)
self.Enable(len(self.__taskList.categories()) > 0)

def onRemoveCategoryFromTask(self, event):
for category in event.values():
if category not in self.__taskList.categories():
self._checkListBox.Delete(self._checkListBox.FindString(category))
self.Enable(len(self.__taskList.categories()) > 0)


Removing this type of duplication is more tricky. Refactoring to something like the following should work (untested):

def updateCategoriesListbox(self, categories, updateNeeded, updateListbox):
for category in categories:
if updateNeeded(category):
updateListbox(category)
self.Enable(len(self.__taskList.categories()) > 0)

def onAddCategoryToTask(self, event):
def updateNeeded(category):
return self._checkListBox.FindString(category) == wx.NOT_FOUND

def updateListbox(category):
self._checkListBox.Append(category)

self.updateCategoriesListbox(event.values(), updateNeeded, updateListbox)

def onRemoveCategoryFromTask(self, event):
def updateNeeded(category):
return category not in self.__taskList.categories()

def updateListbox(category):
self._checkListBox.Delete(self._checkListBox.FindString(category))

self.updateCategoriesListbox(event.values(), updateNeeded, updateListbox)


But this raises the question: is the increase in lines of code and number of functions worth the reduction of duplication? Or did I miss a more simple solution?

Wednesday, September 20, 2006

About this blog

Software development is fun and hard at the same time. This blog discusses my experience with developing an open source desktop application called Task Coach. Task Coach is a task manager that supports hierarchical tasks, budget and time registration, categories, and more. For me, Task Coach is also a vehicle to experiment with software development, tools, and techniques. For example, I try to use test-driven development to steer the development of the software. So far, I like the way test-driven development allows me to add new functionality in a safe and gradual manner. I also like how having an extensive set of automated unit tests (+/- 1700 at the moment, that run in about 30 seconds) allows me to refactor the source code without (too much) fear of seriously breaking the application. Anyway, the plan is to use this blog to talk about ideas about software development, and as much as possible, discuss experience with applying this ideas to Task Coach.