views:

1653

answers:

7

What is this "Execute Around" idiom (or similar) I've been hearing about? Why might I use it, and why might I not want to use it?

+33  A: 

Basically it's the pattern where you write a method to do things which are always required, e.g. resource allocation and clean-up, and make the caller pass in "what we want to do with the resource". For example:

public interface InputStreamAction
{
    void useStream(InputStream stream) throws IOException;
}

// Somewhere else    

public void executeWithFile(String filename, InputStreamAction action)
    throws IOException
{
    InputStream stream = new FileInputStream(filename);
    try {
        action.useStream(stream);
    } finally {
        stream.close();
    }
}

// Calling it
executeWithFile("filename.txt", new InputStreamAction()
{
    public void useStream(InputStream stream) throws IOException
    {
        // Code to use the stream goes here
    }
});

The calling code doesn't need to worry about the open/clean-up side - it will be taken care of by executeWithFile.

This is frankly painful in Java because closures are so wordy - in languages with better closure syntax (e.g. C# lambda expressions, or Groovy) it makes a lot more sense. (In C# this particular case is already handled with using statements, mind you.)

Although "allocate and clean-up" is the typical example given, there are plenty of other possible examples - transaction handling, logging, executing some code with more privileges etc. It's basically a bit like the template method pattern but without inheritance.

Jon Skeet
How is this different from a constructor and descructor which handle initialization and tear-down?
Nathan
It's deterministic. Finalizers in Java aren't called deterministically. Also as I say in the last paragraph, it's not *only* used for resource allocation and cleanup. It may not need to create a new object at all. It's generally "initialization and tear-down" but that may not be resource allocation.
Jon Skeet
So it's like in C where you have a function that you pass in a function pointer to do some work?
Paul Tomblin
This looks like a combination of command and template method to me...
dhiller
I expanded on the concept of EA in Java, how painful it is, and proposed language changes that would make it easier, here: http://metatechnology.blogspot.com/2007/02/raii-and-closures-in-java.htmlThat was early 2007 - looks like closures still haven't made it into Java, though :-(
Phil Nash
Also, Jon, you refer to closures in Java - which it still doesn't have (unless I missed it). What you describe are anonymous inner classes - which are not quite the same thing. True closures support (as have been proposed - see my blog) would simplify that syntax considerably.
Phil Nash
@Phil: I think it's a matter of degree. Java anonymous inner classes have access to their surrounding environment *in a limited sense* - so while they're not "full" closures they're "limited" closures I would say. I would certainly like to see proper closures in Java, although checked (continued)
Jon Skeet
exceptions make that harder. I'm not entirely happy with any of the closures proposals I've seen. They're either clunky for flexibility or simple but restricted :(
Jon Skeet
+2  A: 

An Execute Around Method is where you pass arbitrary code to a method, which may perform setup and/or teardown code and execute your code in between.

Java isn't the language I'd choose to do this in. It's more stylish to pass a closure (or lambda expression) as the argument. Though objects are arguably equivalent to closures.

It seems to me that the Execute Around Method is sort of like Inversion of Control (Dependency Injection) that you can vary ad hoc, every time you call the method.

But it could also be interpreted as an example of Control Coupling (telling a method what to do by its argument, literally in this case).

Bill Karwin
+11  A: 

The Execute Around idiom is used when you find yourself having to do something like this:

//... chunk of init/preparation code ...
task A
//... chunk of cleanup/finishing code ...

//... chunk of identical init/preparation code ...
task B
//... chunk of identical cleanup/finishing code ...

//... chunk of identical init/preparation code ...
task C
//... chunk of identical cleanup/finishing code ...

//... and so on.

In order to avoid repeating all of this redundant code that is always executed "around" your actual tasks, you would create a class that takes care of it automatically:

//pseudo-code:
class DoTask()
{
    do(task T)
    {
        // .. chunk of prep code
        // execute task T
        // .. chunk of cleanup code
    }
};

DoTask.do(task A)
DoTask.do(task B)
DoTask.do(task C)

This idiom moves all of the complicated redundant code into one place, and leaves your main program much more readable (and maintainable!)

Take a look at this post for a C# example, and this article for a C++ example.

e.James
+4  A: 

I see you have a Java tag here so I'll use Java as an example even though the pattern isn't platform-specific.

The idea is that sometimes you have code that always involves the same boilerplate before you run the code and after you run the code. A good example is JDBC. You always grab a connection and create a statement (or prepared statement) before running the actual query and processing the result set, and then you always do the same boilerplate cleanup at the end--closing the statement and connection.

The idea with execute-around is that it's better if you can factor out the boilerplate code. That saves you some typing, but the reason is deeper. It's the don't-repeat-yourself (DRY) principle here--you isolate the code to one location so if there's a bug or you need to change it, or you just want to understand it, it's all in one place.

The thing that's a little tricky with this kind of factoring-out though is that you have references that both the "before" and "after" parts need to see. In the JDBC example this would include the Connection and (Prepared)Statement. So to handle that you essentially "wrap" your target code with the boilerplate code.

You may be familiar with some common cases in Java. One is servlet filters. Another is AOP around advice. A third is the various xxxTemplate classes in Spring. In each case you have some wrapper object into which your "interesting" code (say the JDBC query and result set processing) is injected. The wrapper object does the "before" part, invokes the interesting code and then does the "after" part.

Willie Wheeler
A: 

This reminds me of the strategy design pattern. Notice that the link I pointed to includes Java code for the pattern.

Obviously one could perform "Execute Around" by making initialization and cleanup code and just passing in a strategy, which will then always be wrapped in initialization and cleanup code.

As with any technique used to reduce code repetition, you should not use it until you have at least 2 cases where you need it, perhaps even 3 (a la the YAGNI principle). Keep in mind that the removing code repetition reduces maintenance (fewer copies of code means less time spent copying fixes across each copy), but also increases maintenance (more total code). Thus, the cost of this trick is that you are adding more code.

This type of technique is useful for more than just initialization and cleanup. It's also good for when you want to make it easier to call your functions (e.g. you could use it in a wizard so that the "next" and "previous" buttons don't need giant case statements to decide what to do to go to the next/previous page.

Brian
A: 

If you want groovy idioms, here it is:

//-- the target class
class Resource { 
    def open () { // sensitive operation }
    def close () { // sensitive operation }
    //-- target method
    def doWork() { println "working";} }

//-- the execute around code
def static use (closure) {
    def res = new Resource();
    try { 
        res.open();
        closure(res)
    } finally {
        res.close();
    }
}

//-- using the code
Resource.use { res -> res.doWork(); }
Florin
If my open fails (say acquiring a reentrant lock) the close is called (say releasing a reentrant lock despite the matching open failing).
Tom Hawtin - tackline
+1  A: 

See also Code Sandwiches, which surveys this construct across many programming languages and offers some interesting research'y ideas.

Ben Liblit