July 29, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

Design Pattern: Proxy

  • August 1, 2007
  • By Jeff Langr
  • Send Email »
  • More Articles »

There's not much to the factory:

public class PortfolioFactory {
   public static Portfolio create(User user) {
      return new PortfolioGuard(user.canUpdate(),
                                new PortfolioImpl());
   }

}

PortfolioFactory requires a User object. The User class encapsulates a user ID and information about whether or not a user has update access:

import static org.junit.Assert.*;
import org.junit.*;

public class UserTest {
   @Test
   public void createReadOnly() {
      User user = User.createReadOnly("jlangr");
      assertFalse(user.canUpdate());
      assertEquals("jlangr", user.getName());
   }

   @Test
   public void createWithUpdateAccess() {
      User user = User.create("jlangr");
      assertTrue(user.canUpdate());
   }
}
public class User {
   private final boolean canUpdate;
   private final String name;

   public User(String name, boolean canUpdate) {
      this.name = name;
      this.canUpdate = canUpdate;
   }

   public static User create(String name) {
      return new User(name, true);
   }

   public static User createReadOnly(String name) {
      return new User(name, false);
   }

   public boolean canUpdate() {
      return canUpdate;
   }

   public String getName() {
      return name;
   }
}

The proxy implementation is complete: A PortfolioGuard stands in for a PortfolioImpl, disallowing clients from making purchases when they don't have the proper access rights. A UML class diagram representing this design appears in Figure 1.

A Better Implementation

My simple implementation of the proxy pattern isn't as good as it could be. First, the maintenance work will increase as the Portfolio interface changes. Not only do I need to provide implementations in the PortfolioImpl class, but I also need to provide corresponding delegator methods in PortfolioGuard. Second, I need to track which methods need to be protected and write appropriate guard clauses in the PortfolioGuard class. It'd be nice if I could tag methods directly within the Portfolio interface.

Fortunately, Java provides mechanisms that can help solve both problems. The dynamic proxy mechanism introduced in Java 1.3, although a bit ugly, eliminates the need to continually update PortfolioGuard as Portfolio changes. And annotations, introduced in Java 5, gives me a way of directly tagging "mutator" methods—methods that update Portfolio object state. The changed classes appear in Listing 4. For details on these two Java features, refer to Sun's Java pages, or better, search the web!

Listing 4: Using dynamic proxy

// MutatorTest:
import static org.junit.Assert.*;
import java.lang.reflect.Method;
import org.junit.Test;

public class MutatorTest {
   @Test
   public void nonpresence() throws SecurityException,
      NoSuchMethodException {
      Method method = X.class.getDeclaredMethod("readOnlyMethod");
      assertFalse(method.isAnnotationPresent(Mutator.class));
   }

   @Test
   public void presence() throws SecurityException,
      NoSuchMethodException {
      Method method = X.class.getDeclaredMethod("mutatorMethod");
      assertTrue(method.isAnnotationPresent(Mutator.class));
   }

   class X {
      public void readOnlyMethod() {}
      @Mutator public void mutatorMethod() {}
   }
}

// Mutator:
import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Mutator {
}

// Portfolio:
public interface Portfolio {
   int numberOfHoldings();
   int sharesOf(String symbol);
   @Mutator void purchase(String symbol, int shares);
}

// PortfolioGuardTest:
...
public class PortfolioGuardTest {
   ...
   @Test(expected = RuntimeException.class)
   public void prohibitsMutatorMethodsWhenCanUpdateFalse() {
      boolean canUpdate = false;
      // note how I must now construct portfolio proxies:
      Portfolio portfolio = PortfolioGuard.create(canUpdate, target);
      portfolio.purchase("symbol", 1);
   }
}

// PortfolioGuard:
import java.lang.reflect.*;

public class PortfolioGuard implements InvocationHandler {
   public static Portfolio create(boolean canUpdate,
      Portfolio target) {
      PortfolioGuard guardedPortfolio =
         new PortfolioGuard(canUpdate, target);
      return (Portfolio)Proxy.newProxyInstance(
            Portfolio.class.getClassLoader(),
               new Class[] {Portfolio.class}, guardedPortfolio);
   }

   private boolean canUpdate;
   private Object target;

   public PortfolioGuard(boolean canUpdate, Portfolio target) {
      this.canUpdate = canUpdate;
      this.target = target;
   }

   @Override
   public Object invoke(Object proxy, Method method, Object[] args)
         throws Throwable {
      if (method.isAnnotationPresent(Mutator.class) && !canUpdate)
         throw new RuntimeException();
      return method.invoke(target, args);
   }
}




Page 3 of 4



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel