What is the “Execute Around” idiom?

后端 未结 8 1142
不思量自难忘°
不思量自难忘° 2020-11-22 12:39

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?

相关标签:
8条回答
  • 2020-11-22 13:13

    See also Code Sandwiches, which surveys this construct across many programming languages and offers some interesting research’y ideas. Concerning the specific question of why one might use it, the above paper offers some concrete examples:

    Such situations arise whenever a program manipulates shared resources. APIs for locks, sockets, files, or database connections may require a program to explicitly close or release a resource that it previously acquired. In a language without garbage collection, the programmer is responsible for allocating memory before its use and releasing it after its use. In general, a variety of programming tasks call for a program to make a change, operate in the context of that change, and then undo the change. We call such situations code sandwiches.

    And later:

    Code sandwiches appear in many programming situations. Several common examples relate to the acquisition and release of scarce resources, such as locks, file descriptors, or socket connections. In more general cases, any temporary change of program state may require a code sandwich. For example, a GUI-based program may temporarily ignore user inputs, or an OS kernel may temporarily disable hardware interrupts. Failure to restore earlier state in these cases will cause serious bugs.

    The paper does not explore why not to use this idiom, but it does describe why the idiom is easy to get wrong without language-level help:

    Defective code sandwiches arise most frequently in the presence of exceptions and their associated invisible control flow. Indeed, special language features to manage code sandwiches arise chiefly in languages that support exceptions.

    However, exceptions are not the only cause of defective code sandwiches. Whenever changes are made to body code, new control paths may arise that bypass the after code. In the simplest case, a maintainer need only add a return statement to a sandwich’s body to introduce a new defect, which may lead to silent errors. When the body code is large and before and after are widely separated, such mistakes can be hard to detect visually.

    0 讨论(0)
  • 2020-11-22 13:13

    I'll try to explain, as I would to a four year old:

    Example 1

    Santa's coming to town. His elves code whatever they want behind his back, and unless they change things get a little repetitive:

    1. Get wrapping paper
    2. Get Super Nintendo.
    3. Wrap it.

    Or this:

    1. Get wrapping paper
    2. Get Barbie Doll.
    3. Wrap it.

    ....ad nauseam a million times with a million different presents: notice that the only thing different is step 2. If step two is the only thing that is different, then why is Santa duplicating the code, i.e. why is he duplicating steps 1 and 3 one million times? A million presents means that he is needlessly repeating steps 1 and 3 a million times.

    Execute around helps to solve that problem. and helps eliminate code. Steps 1 and 3 are basically constant, allowing for step 2 to be the only part that changes.

    Example #2

    If you still don't get it, here is another example: think of a sandwhich: the bread on the outside is always the same, but what's on the inside changes depending on the type of sandwhich you choose (.e.g ham, cheese, jam, peanut butter etc). Bread is always on the outside and you don't need to repeat that a billion times for every type of sandwhich you are creating.

    Now if you read the above explanations, perhaps you will find it easier to understand. I hope this explanation helped you.

    0 讨论(0)
  • 2020-11-22 13:14

    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.

    0 讨论(0)
  • 2020-11-22 13:15

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

    0 讨论(0)
  • 2020-11-22 13:20

    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.

    0 讨论(0)
  • 2020-11-22 13:25

    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(); }
    
    0 讨论(0)
提交回复
热议问题