Whither the Successor in the Chain of Responsibility
The successor chain is kinda important in the Chain of Responsibility. So where it is located is important. Thankfully, this is an extremely flexible pattern that adapts to our needs…
Video transcript & code
(If you're looking for the account upgrade offer mentioned in the video, scroll to the bottom for details.)
Welcome to the fourth and last episode in our miniseries with guest chef Chris Strom and his Compendious Thunks. You may recall that previously, Chris demonstrated how the chain of responsibility pattern can eliminate conditionals from our code. In today’s episode, Chris begins to elaborate on the chain of responsibility, showing a variation that can make extending the chain more convenient.
Just as a reminder, these videos use the Dart programming language for their examples. You probably won’t have any trouble following along. But if you do want a quick orientation, jump back to episode #461 for a 5-minute introduction to dart.
If you’ve enjoyed this series of design patterns videos, stay tuned at the end of today’s video. I have some news that I think you’re really going to like.
Now, here’s Chris.
The Chain of Responsibility is a pretty chill pattern. Yeah, I said it. And it's totally true. Because for all of its power—for all of its beauty in cleaning ugly conditionals—it's a very laid back, easy-going pattern that works well in a variety of situations.
The chill factor of the Chain of Responsibility is going to be a recurring theme as we discuss the pattern. In this episode, we'll look at flexibility in where the successor chain gets implemented. There's not an absolute right or wrong—and sometimes you won't even have a choice. But location has an impact, so it's very much worth discussing up front.
So far, our successor chain has been implemented in the concrete image classes--
GifImage, etc. The definition of the successor is located in the
Image handler baseclass, but the actual call to the successor in the chain is in the concrete subclasses.
[embed_dartpad id="663b696c6cd90d6ae4140ebccd7223e9" title="The concrete handlers have the job of calling the next in line…"][/embed_dartpad]
Let's move it instead into the handler base class. This changes the
import() definition in the handler baseclass from merely providing the interface, to also providing the successor chain implementation. That is, it sends the
import() request along to the next link in the chain.
With that, the baseclass defines the request interface (
import with no arguments), the successor chain (
nextImporter), and the successor chain implementation (calling
import()). Simple enough.
The result isn't a profound change, but it has an impact on the existing implementation as well as how we approach new requests. To see how, look at
We added this as a façade class—as an approachable entry point to the chain. It never does anything on its own, instead just forwarding the request onto the "real" first concrete handler—
JpegImage in this case. And look, it processes the successor chain the same, exact way that the handler baseclass now does.
In other words, we don't need to declare an
import() method in
ImageImporter at all. We can just let the identical implementation in the baseclass handle it. Put another way, by not declaring a handler method, a concrete handler is indicating that it never handles this kind of request. It's a nice little win--thanks to moving the successor into the handler baseclass.
Now, we absolutely do not want to maintain the successor chain implementation in two locations. So concrete handlers that do handle requests now need to defer to the baseclass when they wish to ignore specific requests. That is, when the
JpegImage is told to import a GIF image, it'll send that request back to the
Image handler baseclass, which can then forward the request to the successor in the chain.
After doing the same for
PngImage, we again have working code. And the successor chain is defined entirely in the handler baseclass.
At the risk of jumping ahead... in the next episode, we talk about supporting multiple requests. In the context of this episode, what happens if we wanted to support a request only in one concrete handler. For example, what if, hypothetically, we wanted the ability to import only JPEG images.
With the successor chain in the handler baseclass, it's trivial. We define
importOnlyIfJpeg() in the handler, which forwards the request to the successor. Then we only need define the request handler in the concrete handlers that have any interest: in
JpegImage and the handler of last resort,
UnknownImage. None of the other image handlers need be concerned at all. And we never once used the
if keyword. Nice!
[embed_dartpad id="902fac30822497c9c46c27efa28b2863" title="The successor now lives in the handler… because it can."][/embed_dartpad]
The successor chain's location is one of those pattern things that doesn't have a right-or-wrong answer, but still deserves some thought. Putting it in the handler baseclass yields a nice convention and can save some code, which is a welcome double win.
But don't sweat it too much if the implementation stays in the concrete classes—it's still a fairly natural approach (as we saw in the last episode). Existing code that pre-defines the successor often doesn't give you the option.
In this episode, we saw a trivial second request added to a Chain of Responsibility implementation. In the next episode we'll look at more complex (but still compendious!) requests. Some of them will even be frighteningly familiar to you web and UI programmers out there. So yeah, it's gonna be some spooky, thunky, chill fun!
Hey, it’s Avdi again. Well, I’m afraid that brings us to the end of our Compendious Thunks crossover series. We’ve barely scratched the surface in the last four episodes. Both the Command Pattern series and Chain of Responsibility series contain several more videos, and that’s not even mentioning all the other design patterns Chris covers.
After this taste, are you eager to see more? Well I have some good news, and some great news.
The good news is that the full Compendious Thunks series 1 course is now available in the RubyTapas library. The great news is that if you are a Ravenous-level subscriber, or if you are part of a non-legacy team membership, you already have immediate access to this course. That’s right, you can head on over to our course listing page right now, and start working your way through the episodes you haven’t seen yet.
One thing I should make clear: Chris is still making these videos, and they will be coming to RubyTapas on a slight delay. So you won’t be able to binge-watch the entire series right now. But the whole 6-episode Command Pattern mini-series is already there for your immediate enjoyment, and we’ll be steadily adding the rest over time. My personal recommendation is that you space these videos out one or two per week, because they are a lot to absorb all at once.
If you’re not yet a Ravenous-level subscriber, but this is tempting you to upgrade, I’d like to offer you an extra enticement: in celebration of the new course, I’m making an upgrade discount available. For details, just scroll down to the bottom of this episode’s page. The discount will only available for a limited time, so you might want to take advantage sooner rather than later.