I recently ran into an interesting design challenge involving the Resource Acquisition Is Initialization (RAII) idiom and the Composite design pattern.

Consider a simple Switch class that models an on/off switch:

Simple Switch
class Switch
def initialize
@on = false
end
def on?
@on
end
def turn_on
@on = true
end
def turn_off
@on = false
end
end

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:

Explicitly Turn Off the Switch
switch = Switch.new
switch.turn_off
perform_action

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:

Restore Switch State
switch = Switch.new
was_on = switch.on?
switch.turn_off
perform_action
switch.turn_on if was_on

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.

Instead, we can use the RAII idiom that I wrote about earlier and spoke about at MWRC 2014 and add a method to Switch to help us with this:

RAII Idiom in Switch
class Switch
#...
def be_off_while
was_on = on?
turn_off
yield
ensure
was_on ? turn_on : turn_off
end
#...
end
#...
switch = Switch.new
switch.be_off_while { perform_action }

That’s better.

Now, what if we had a bank of switches, and we needed all of the switches to be off while performing the action?

Bank of Switches
switch1 = Switch.new
switch2 = Switch.new
switch3 = Switch.new
#...
switch1.be_off_while do
switch2.be_off_while do
switch3.be_off_while do
perform_action
end
end
end

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:

Composite Switch Bank
class SwitchBank
def initialize(switch_count)
@switches = Array.new(switch_count) { Switch.new }
end
def on?
@switches.any?(&:on?)
end
def turn_on
@switches.each(&:turn_on)
end
def turn_off
@switches.each(&:turn_off)
end
def be_off_while
# ???
end
end

Most of 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 Switch:

Duplicating be_off_while
class SwitchBank
#...
def be_off_while
was_on = on?
turn_off
yield
ensure
was_on ? turn_on : turn_off
end
#...
end

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 delegating to Switch’s be_off_while method? How can we marry an iteration of SwitchBank’s switches with nested block calls?

Here’s a way I found to solve the problem:

Marry Iteration With Nested Blocks
class SwitchBank
#...
def be_off_while
nester = ->(switches) do
return yield if switches.empty?
switches.shift.be_off_while { nester.call(switches) }
end
nester.call(@switches.dup)
end
#...
end

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 be_off_while 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.