Consider a simple
Switch class that models an on/off switch:
Let’s say we have some actions that must be performed when the switch is off. We could write the code explicitly every time we perform any of the actions:
This works, but forces the switch off and leaves it that way. That is very likely to mess up other code, so it would be better to make sure we restore the original state of the switch when we’re done with it:
That’s a bit better, but still leaves the switch off if
perform_action raises an exception, and it doesn’t return the switch
to the off state if something in
perform_action turns it on, so we’d
really want to use an ensure block and yet more code. That’s a lot of
duplication every time we need to temporarily turn off the switch.
Now, what if we had a bank of switches, and we needed all of the switches to be off while performing the action?
I guess that works, but it’s pretty fragile if we decide to change how many switches are in the bank. If we’re going to be managing banks of switches a lot, we can turn to the Composite pattern:
SwitchBank is easy to write; we have to decide whether the
bank is on or off if only a few of the switches are on (I chose “on”
here). But how do we write
be_off_while for this case?
One option is to duplicate the implementation from
Duplication is often a code smell, and this case is no different, but
here there’s not much code, so it’s borderline. What if
be_off_while was a much more complex method? Duplication would be
worse in that case. Is there a way we can implement this by
be_off_while method? How can we marry an
SwitchBank’s switches with nested block calls?
Here’s a way I found to solve the problem:
Here we’re using a lambda that calls itself recursively. With
recursion, there’s always a danger of running out of stack space. For
this situation we’ll assume that a
SwitchBank won’t have enough
switches to cause a problem, but it is something to be aware of.
The lambda takes a collection of switches starting with a copy of the original array. The lambda destructively modifies the collection, so we need to make sure we’re working on a copy.
If the collection is empty (the base case of the recursion), we simply
yield to the block, performing the desired action.
Otherwise, we remove the first element from the array and call its
be_off_while method, nesting a recursive call to the lambda inside.
This recursive call is made on a collection that is now one element
shorter than it was (because we just
shifted an element out of it).
Because the collection is always one element shorter, we can guarantee
that the recursion will eventually terminate.
The net result is that we nest calls to each element’s
method and ultimately perform the desired action at the innermost
level of nesting.
I found this to be an interesting problem to solve. A functional programming expert could probably come up with a better solution than this.
I originally solved this problem in Smalltalk, but translated to Ruby for this post. In both of these languages, I find this solution a little too clever and I probably wouldn’t keep this code around for long.
For my original case, the surrounding context suggested a different way of attacking the problem, so I ended up not keeping this code.