Tuesday, December 6, 2011

Data Payload Facet Pattern

I have no idea if this already exists, or if there's a better way, but here's a little thing I came up with yesterday.  I'm calling it the data payload facet pattern. I guess it's pretty similar to the Façade pattern, but a bit more specific.

What is this pattern?  It's a way of creating what is essentially an Object Oriented version of a SQL view.

If you have a generic payload object that has a Map<String,Object> concept, then anything you set or get from this Map is, to a point unsafe.  You can use the class argument improvement at least on getters, but it's still pretty weak.

Enter the Payload Facet Object.
 
public class DataPayload {
  properties = new HashMap<String,Object>();
  private Object payload;

  public <T> T getProperty(String name, Class<? extends T> c) {
    if (!c.containsKey(name) || c.isInstance(properties.get(name))) {
      return properties.get(name);
    }
    throw new IllegalArgumentException("The property "+name+" has type "+properties.get(name).getClass().getName()+" but you requested an object of type "+c.getName());
  }

  public DataPayload setProperty(String name, Object value) {
    properties.put(name, value); // not worrying about replacing here
    return this;
  }
}

So we have the pretty awful property object that is horribly error prone, particularly with String names.

We layer a Facet Object on top of this:

public class UserEntryPayloadFacet {
  public static final String USERNAME="Username";
  public static final String PASSWORD="Password";
  public static final String EMAIL="Email";

  private DataPayload proxied;

  public String getUsername() {
    return super.getProperty(UserEntryPayloadFacet.Username, String.class);
  }

  public UserEntryPayloadFacet setUsername(String username) {
    super.setProperty(UserEntryPayloadFacet.Username, username);
    return this;
  }

  public String getPassword() {
    return super.getProperty(UserEntryPayloadFacet.Password, String.class);
  }

  public UserEntryPayloadFacet setPassword(String password) {
    super.setProperty(UserEntryPayloadFacet.Password, password); // of course - passwords are never in plain text, right?
    return this;
  }
  
  ....
  // If you feel like it...
  public DataPayload unwrap() {
    return proxied;
  }
}

You get the general idea.  In my implementation I've defined an interface to include an unwrap() method call to get at the payload, and in my case, I'm also exposing the underlying data object properties via proxy method calls, so the facet would have a getProperty(...) call and a setProperty(...) call.  Your mileage may vary.

No comments:

Post a Comment