Using Chain of Responsibility in REALbasic

| | Comments (10)

So you're writing a complex application and you stumble across a problem. You're coding the Action event for some menu or button and you realize that you don't have access to the instance of some other object where you want the action to occur. For example, you push a button and you want a floating window to display a picture -- but you don't have a reference to the floating window in question. So how do you deal with this sort of situation? You use the Chain of Responsibility pattern, of course! I tend to (mistakenly) refer to this as the Command pattern because the basic concept is that you send "commands" out into the ether, and whoever is responsible for handling that command will get the message and deal with it.

So to be more clear -- you use this pattern whenever you want an arbitrary object to handle a command, but you don't have direct knowledge of the object. However, there's a side benefit to using this pattern, which is that your application becomes easier to control (a topic which I will discuss a little later). However, there is a downside to using this pattern. Because you don't know what object is going to handle the command, you can't be certain of whether any object will handle it. It's possible that you throw a command out that gets dropped on the floor because nothing handles it.

The way this pattern works is quite simple. There's a main module that is responsible for dispatching commands to all the windows in your application. So the caller simply says "Dispatch this message", and then windows begin getting a "message dispatched" event so they can handle it. Once a window handles the message, the entire process stops. So let's take a look at how the code works.

First, we need to be able to tell which windows are able to handle commands. To do that, we need an Interface:
[rbcode]
Interface Commandable
Function HandleCommand( cmd as String ) as Boolean
End Function
End Interface
[/rbcode]
That's pretty simple -- there's one function that the window needs to implement, which is the function to handle commands. Commands need some sort of unique identifier, so we'll use strings to clarify which message occurred. You could use an Integer if you'd like, but I like using strings because they're easier to work with (you can print debug information about them much easier).

Before we continue, I want to talk about how to make a window implement an interface. You need to make a new Class, set its Super to Window and then make the class implement the interface. Then, take your Window object and set its Super to the new class you just made. If you'd like, you can override the class' interface implementation (so that you can have access to everything in the window such as controls). It's a royal mess. Thankfully, in RB 2005 the process got *much* easier -- just tell the window to implement the interface in the project editor tab like you would with any other class. Continuing on....

Now we know what function we're calling, so let's take a look at how the dispatcher works. It simply needs to loop over windows to see which ones are commandable and then call HandleCommand on them. Check it out!
[rbcode]
Module Commands
Protected Sub Dispatch( cmd as String )
Dim i, count as Integer

// Get the number of windows so that we can loop over them
count = WindowCount
for i = 0 to count - 1
// Check to see if the window is commandable or not
if Window( i ) IsA Commandable then
// If the window handles the command, then we should
// bail out.
if Commandable( Window( i ) ).HandleCommand( cmd ) then return
end if
next i
End Sub
End Module
[/rbcode]
As you can see, the method is quite simple. It loops over all the windows in the project and looks for the ones which are Commandable. When it finds one, it calls HandleCommand. If the call returns true, then we're done (the command was handled), otherwise we continue on to the next window in the list.

Now that we have the building blocks for the pattern done, let's make it much more powerful. The astute reader may have noticed there are two places we can improve this concept. The first way we can make it more powerful is by including controls in our list of things to search. By doing this, you can have a custom control on a window which is in charge of handling a command. For example, you could have a GraphCanvas class which handles the "New Data Point" command. If we leave things the way they are then then window is responsible for handling the command and passing the information along to the canvas. This is too tight of coupling for my tastes though. The other improvement to make stems from the fact that a string command doesn't always give enough information. For example, let's say you want to make a command called "Open Document". Simply knowing that you need to open a document isn't enough information to handle the command; you need information about what file to open. So we're going to give ourselves more information by creating a Command class which others can then subclass.

The Command class will continue to encompass the string data type. In fact, you will use the class as it if really were a string with copious use of operator overloading. However, you can subclass the Command class and add on any extra information you need. So let's take a look at this special class, and how to properly use it.
[rbcode]
Class Command
Protected Dim mType as String

Function Operator_Compare( rhs as String ) as Integer
// If the types match, then return 0 to say we're "equal"
if rhs = mType then return 0

return -1
End Function

Sub Operator_Convert( rhs as String )
// Assign the given string to our type
mType = rhs
End Sub
End Class
[/rbcode]
That's all there is to it. We overload the comparison operator so that we can be compared to a string. This lets a Command object be used in a select case statement or if statement without having to muck around with the internals of the class. We also overload the conversion operator so that the user can assign a string to a Command object. Let's see how you'd properly construct and use one of these classes.
[rbcode]
// Creating one
Dim cmd as new Command = "Ring the Bell"

// Using one
select case cmd
case "Ring the Bell"
Beep
end select
[/rbcode]
And most importantly, how do you properly subclass a Command object so that it can be used properly?
[rbcode]
Class OpenDocumentCommand Inherits Command
Dim mFile as FolderItem

Sub Constructor( f as FolderItem )
mType = "Open Document"
mFile = f
End Sub
End Class

// Using it now
select case cmd
case "Open Document"
App.OpenDocument( OpenDocumentCommand( cmd ).mFile )
end select
[/rbcode]
You may be wondering, "how does Aaron typecast a string (cmd) into an OpenDocumentCommand?" Simple! We're going to change the dispatcher so that it works with Commands instead of strings. Again, we're going to be sneaky about it though since you probably don't want to have to make new Command objects every time manually. Here's the new dispatcher code:
[rbcode]
Module Commands
Private Dim mHandled as Boolean

Sub Dispatch( cmd as Command )
// We want to loop over everything in the system
// and give it a chance to handle the command.
mHandled = false

// Loop over every window and give it a shot
dim i, count as Integer
dim j, childCount as Integer
dim wnd as Window

count = WindowCount
for i = 0 to count - 1
// If the window itself is commandable, then we
// need to fire it on the window
wnd = Window( i )

// First loop over all the child controls of the window
// and make sure they get their shot too
childCount = wnd.ControlCount
for j = 0 to childCount - 1
if wnd.Control( j ) IsA Commandable then
if Commandable( wnd.Control( j ) ).HandleCommand( cmd ) then
mHandled = true
return
end if
end if
next j

// Then check the window itself
if wnd IsA Commandable then
if Commandable( wnd ).HandleCommand( cmd ) then
mHandled = true
return
end if
end if
next i

// Finally, ask the App class if it wants to handle it
if App IsA Commandable then
if Commandable( App ).HandleCommand( cmd ) then
mHandled = true
return
end if
end if
End Sub

Sub Dispatch( type as String )
// Make the command object
dim cmd as new Command
// Set it's type
cmd = type
// Dispatch it
Dispatch( cmd )
End Sub

Function CommandWasHandled() as Boolean
return mHandled
End Function

End Module
[/rbcode]
You'll notice that we're now dispatching to child controls of each window as well as the App class (if appropriate), and that we're overloading the Dispatch method. One takes a Command object (so that you can do things like the OpenDocumentCommand), and the other takes a string (so you can make generic command objects still). You'll also notice that I snuck in a way for you to tell whether the last command was handled or not.

The last thing to look at is how to properly work with the HandleCommand function. This is the part where all the real work happens, so the example is going to be rather contrived.
[rbcode]
Class SomeClass
Function HandleCommand( cmd as Command ) as Boolean
select case cmd
case "New Document"
App.NewDocument

case "Open Document"
App.OpenDocument( OpenDocumentCommand( cmd ).mFile )

case "Beep"
Beep

else
// We don't handle this command, so bail out
return false

end select

// If we got here, it's because we handled the command
return true
End Function
End Class
[/rbcode]

Now you're ready to use the extremely powerful Chain of Responsibility design pattern! But you may be wondering, why would I use this and what makes it so powerful?

When used properly, this design pattern allows you to do things like easily script your application. Imagine having a console window where you could construct commands and dispatch them in a generic fashion? This way you don't have to fuss with the UI to test functionality out. Or you can add power features that aren't ready to be exposed yet. Or you can add testing features (like DebugDumpObjects) without bothering to add UI to do so. You could also construct commands from command line parameters and automate the way your application works. Any number of very useful things can be done by using this design pattern. And I can attest for its ease of use -- we use this pattern extensively in RB 2005. For example, every menu item dispatches a command instead of handling the functionality in the menu handler itself. This allows us to decouple the function from the UI so we can have the same functionality when you press a toolbar button as you get when you use a contextual menu, or a real menu, etc.

To see how this works, I made a sample project that you can download here.

10 Comments

I haven't read through the article (too tired to think), but it seems from your summary it's similar to NSNotificationCenter stuff in Cocoa?

-- SirG3

Not certain -- I've never done any Cocoa programming. But I'm sure Jon or some other Mac-person will chime in with the proper answer.

Aaron,

This is a superb post, very enlightening, I just wish I'd seen it six months ago, would have made my life so much easier!

Thank you very much for posting these pattern examples and all your other little tutorials, they are fantastic and have helped me immensely.

--
Ian

This is great Aaron! I have a .NET app I wrote for a client a couple of years ago that could have benefited greatly from this pattern. Keep the pattern articles coming!

Thanks for including the RB sample too. That helps RB noobs like me out a lot. It's can be a little disorienting learning a new IDE.

When do you plan to cover the Whip of Obedience pattern?

ROFL! Brady, you crack me up.

I have a question about iterating over the controls in every window from the Dispatch function. Wouldn't it be better to have a Window subclass that takes a command and propagates it to its child controls? That seems like a cleaner way to implement this pattern to me.

I think that design is valid as well. The subclass could first loop over all children and see if they handle it, and then pass the message up to the subclass via an event.

In fact, I think that's an easier solution for RB 5.5 and before due to the window-implementing-interface snafu.

However, in RB 2005, it's so easy to just have a window implement the interface, I just went with the current design. To be honest, I wrote the original code in RB 2005, and I didn't think about the interface issue until I was writing the article. :-P

Most enlightening - learned a lot.


For what it's worth, it takes me back to very old days (that is, my was I ever that young days) working with the (Mac) THINK Class Library (Object Oriented Pascal), though REALBasic's automatic garbage collection and operator overloading makes this much cleaner.


I've been putting this in my head alongside the Observer model, from where I've been with Java -- in broad terms (to my somewhat limited understanding), early Java AWT/Swing did its control handling on the Chain of Responsibility pattern, and then from 1.2 switched to the Observer (Model-View-Controller) pattern.

I guess it's different approaches to the design problem as put in the first paragraph: writing a Control's event handler is easy when the intended recipient is visible from an enclosing Window ... but what if it isn't? Under Chain of Responsibility, you set up a search/dispatch tree (across and into Commandables) to see that the command reaches the recipient; under Observer the programmer is expected to write an actionListener and register it with the Control.

On the face of it, the Observer pattern would appear to be faster (since the command isn't scudding through the ether looking for a catcher) ... but since we're dealing with an extreme situation in the first place (ie not just coding an event handler in a Window). I find my Java code gets a little cluttered and certainly a bit tedious in having to define and code all these actionListeners, even as anonymous or inner classes. Similarly, Observer-style command dispatch might be elegant and fast once the controls are hooked up to their actionListeners ... but getting them hooked up in the first place might involve voodoo or at least very careful and potentially fragile code (the visibility issue).


The other point that comes to mind (in what is turning into a rather long post - sorry) is how this can be linked to Undo capability. It seems to me that this could be done by defining a subclass of the Command interface called UndoableCommand, which the programmer would implement to say how the Command is undone (eg a "MoveTo" x, y Command would have a subclass implementation that recorded xOld, yOld and hence generated a "MoveTo" xOld, yOld Command).

The Dispatch module would then be extended to store Undo and Redo stacks to log Commands thus:
- If the Command is not an UndoableCommand, warn the user that the command isn't undoable and then clear the stacks.
- Else push the Command's Undo onto the Undo stack.
- When "Undo" is activated, do the appropriate monkeying to the Redo stack.

Undo management via this "capturing of commands" would probably need to be compared with "captuing of state" on an app-by-app basis -- depends on whether the app makes it easier to be capturing/storing absolute states (under Observer say) or relative changes (Chain of Responsibility commands).

In the vague hope that it's useful,

- CA205.

I think trying to use the Chain pattern to create an Undo stack is a very interesting idea. I think it would cause for a tighter coupling between actions though (since you now need to have two commands for any undoable action, and the HandleCommand function would now need to implement two commands for each undoable action. So I'm not too certain I would like that.

But you are right, you tend to use these patterns together. For example, we use the Chain pattern + Observer and Command + Observer.

Leave a comment

Disclaimer

I'm currently an employee of REAL Software. My blog is mine. The opinions represented in this blog are mine as well and may not represent my employer's opinions. All original material is copyrighted and property of the author.

REALbasic® is a registered trademark of REAL Software, Inc. REAL SQL Server™ and Lingua™ are pending trademarks of REAL Software, Inc. All rights reserved.