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?