Archive for May, 2010

Separating Drag-and-Drop from View components

Wednesday, May 12th, 2010

I was recently working on a set of view classes that, among other things, implemented custom drag-and-drop behavior. As we all know, Drag-and-drop requires several different event handlers for the various drag events (DRAG_START, DRAG_ENTER, etc.). Adding these event handlers to your view class adds a lot of bulk to the existing view logic.

In addition to the issues that are directly related to the sheer size of the class, including drag-and-drop logic in a view can often be considered a 2nd responsibility of the view. I often find that, when I’m working on a view that contains drag-and-drop behavior, I’m typically doing one of two things:

  1. Working on the view (look-and-feel, data set-up, gathering results after user interaction, etc.).
  2. Working on the drag-and-drop behavior.

Thus, if I am working on the view, all of the drag-and-drop logic is excess clutter in my way (and vice versa).

This should actually be an immediate red flag. Good coding practice suggests that a class should have a single responsibility. The Single Responsibility Principle, a term coined by Uncle Bob, correlates a “responsibility” with “a reason to change”. In other words, a class should have only one reason to change. In this example, there are two reasons that I might change the view. Therefore, one of the reasons-for-change should be refactored out of this class. It seems silly to refactor the view out of the view class. So, my approach was to move the drag-and-drop functionality into its own class.

There are probably a number of ways to structure the association between the view and the class containing the drag-and-drop functionality. However, one simple method is to inject the view component into the drag-and-drop class. This will allow the drag-and-drop class to register for the drag events emitted by the view. To limit coupling, the injected reference can be typed as a generic UIComponent (to have access to the drag events).

private var viewComponent:UIComponent;

public function DragAndDropFunctionality(component:UIComponent) {
	viewComponent = component;
	viewComponent.addEventListener(DragEvent.DRAG_ENTER, onDragEnter, false, 0, true);
	viewComponent.addEventListener(DragEvent.DRAG_EXIT, onDragExit, false, 0, true);
	viewComponent.addEventListener(DragEvent.DRAG_DROP, onDragDrop, false, 0, true);
}

Some events, such as DRAG_DROP will require the view to take action (add the dropped item to the appropriate place). Again, we want to limit coupling in our communication. The standard technique to maintain separation is for the lower-level component (the drag-and-drop class, in this case) to dispatch events. Then, the higher-level component (the view, in this case) can listen for those events and take the appropriate action.

Once this separation is achieved, we will have classes that have (hopefully) a single responsibility. When you need to work on the drag-and-drop logic, you can go directly to the drag-and-drop class. I find it much easier to follow the drag-and-drop logic without the view code cluttering the class. Similarly, the view component is no longer cluttered with all of the drag-and-drop event handlers. The classes are smaller and more obvious. And, the two “responsibilities” can now be worked on by multiple developers without lock conflicts or merging, to boot.