Octopull/Java: Execute Around Method and Proxy Goulash
Home C++ articles Java articles Agile articles Bridge lessons

Execute Around Method and Proxy Goulash - by Alan Griffiths

A recent design discussion resulted in a solution that has elements with strong similarities to the "Execute Around Method" and "Proxy" patterns while, in both cases moving outside the scope of the usual descriptions of these patterns [Henney2001, GOF1995].

This article discusses the scenario we encountered; notes the similarities and differences to the canonical forms of "Execute Around Method" and "Proxy" patterns; and, raises the question "are these still the same patterns - or have they been cooked beyond recognition?

Intent

To grant access to specific functionality only between paired operations.

Motivation

When updating large amounts of state it may be desirable to ensure that a "before" and "after" messages are sent to the owner of that state to ensure that any necessary preparation or cleanup may be applied - or that concurrent operations can be inhibited.

Consider a batch update of the product lines available within a system. A complete list of product lines is supplied by an external source and used to create, amend or delete product lines available within the system. Because the only indication that a product is to be deleted is that no data is supplied it is necessary to accumulate information regarding the product lines accessed during the update and to deal with deletions when the update completes.

Typically the product lines are be stored as rows within an RDBMS and accessed via a Broker class that provides the persistence mechanism. The Broker provides a finer grained and more extensive interface than is required by the needs of the application and access normally mediated by a Director that acts as a Façade [GOF1995] for this and Brokers for related objects. (In an EJB based system the Director would be a stateless session bean and a Broker used in place of an entity bean to avoid the cost of unwanted synchronisation.)

In keeping with its role as a Façade the Director should have the responsibility of calling "start" and "finish" methods on the Broker before and after processing of the batch update. However, receipt of the update is not the responsibility of the Director and, in fact, is driven another part of the system entirely. (It might be possible to wrap the processing of the input as an "iterator" passed to the director - however this would add complexity to the system.)

This leads us towards a variation of "Execute Around Method" where the paired operations are "start" and "finish" method calls (not resource allocation) and whose target is the Broker (not self invocation on the Resource object as described by Kevlin Henney [Henney2001]). We still have Kevlin's Command object - but not only has Resource been split into Director and Broker it is not passed to the Command's applyTo method ("run" in Kevlin's paper) method.

However, we've not yet resolved the full context - as the implementation of the Command interface still won't have access to the necessary Broker methods. But this is where our variation on Proxy comes into play: we define a ProxyBroker class that, being in the same package as the Broker, has access to the necessary functionality and can implement public forwarding functions. It is this (not a resource) that is passed to the Command's applyTo method.

This usage of Proxy differs from that described in [GOF1995] because additional functionality is exposed by the Proxy class. Specifically it doesn't substitute for a Broker. (I feel it is far closer to Proxy than to Adapter or Bridge.)

Participants

Consequences

The client code in UpdateCommand.applyTo () has access to the Broker.update() method via the BrokerProxy. The Director is able to ensure that start() and finish() are invoked at the appropriate points in the executeAround method.

Sample code

The following code illustrates the implementation of this dish in Java. We'll assume that a product line comprises its name and price:

public class Product {
    private String name;
    private int price;

    public Product(String name, int price) {

        this.name = name;
        this.price = price;
    }

    public String getName() {
        return name;
    }
    
    public int getPrice() {
        return price;
    }
}

Managing the persistent storage for products a Broker class called ProductBroker this provides methods for retrieving Product information, and for updating the product table:


class ProductBroker {

    // ...

    List listProductNames() {
        // ...
    }

    Product getProduct(String name) {
        // ...
    }

    void startUpdate() {
       // ...<
    }


    void update(Product data) {
        // ...
    }

    void finishUpdate() {
       // ...
    }
}

Because the broker doesn't provide public access we provide public access to the update method via the Proxy class:

public class ProductBrokerProxy {

    // ...

    public void update(Product data){

        broker.update(data);
    }
}

The Command interface by which the client code supplies the update logic is:

public interface ProductUpdateCommand {

    public void applyTo(ProductBrokerProxy target);
}

All of this is co-ordinated by the ProductDirector facard as follows:


public class ProductDirector {

    // ...

    public void updateProducts(ProductUpdateCommand command) {
        final ProductBrokerProxy proxy = new ProductBrokerProxy();

        synchronized (this) {

            broker.startUpdate();
            command.applyTo(proxy);
            broker.finishUpdate();
        }
    }
}

While there appear to be a lot of pieces to the implementation of the final design the client code is clear and does not rely on following a "start/update/finish" protocol for correctness.

public class UpdateProducts {
    
    // ...
    
    public static void main(String[] args){

        // Set up some example data...
        final Vector data = new Vector();
        data.add(new Product("beans", 27));
        data.add(new Product("chicken", 525));

        // Process the example data...
        final Iterator iter = data.iterator();

        director.updateProducts(new ProductUpdateCommand() {
            public void applyTo(ProductBrokerProxy target) {
        
                while(iter.hasNext()){
                    target.update((Product)iter.next());
                }
            }
        });
    }
}

Acnowledgements

Thanks to Andrew Rigley and Jason Martin who: brought the motivating example to my attention; allowed me to participate in an interesting design session that led to the above resolution; and, reviewed the draft article. Also thanks to Jason who supplied the sample code on which the above fragments are based.

<<email addresses for Andy and Jason ommitted on website>>

References

[Henney2001] "Another Tale of Two Patterns" - Kevlin Henney - Java Report March 2001
http://www.two-sdg.demon.co.uk/curbralan/papers/AnotherTaleOfTwoPatterns.pdf

[GOF1995] Design Patterns - Gamma, Helm, Johnson, Vlissides - ISBN 0-201-63361-2


Copyright © 2002 Alan Griffiths