27 January, 2009

Rethinking Java Beans

Lately I've been challenging all of the conventions that we follow. One that has bothered me for a while is our obsession with the bean pattern. The Java Bean pattern originated around building GUIs. It made for an easy way to use reflection to read the properties of an object. However, the pattern is heavy for for the most common usage, which is basically just a Java version of a C struct.

For example, see the following C code:

struct cartUser {
    char firstName[256];
    char lastName[256];
    char addressOne[256];
    char addressTwo[256];
    char city[256];
    char state[2];
    char postalCode[9];
};

Compared to the equivalent Java (convention):

public class CartUser {
    private String lastName;
    private String firstName;
    private String addressOne;
    private String addressTwo;
    private String city;
    private String state;
    private String postalCode;
    public String getLastName() {
        return lastName;
    }
    public String getFirstName() {
        return firstName;
    }
    public String getAddressOne() {
        return addressOne;
    }
    public String getAddressTwo() {
        return addressTwo;
    }
    public String getCity() {
        return city;
    }
    public String getState() {
        return state;
    }
    public String getPostalCode() {
        return postalCode;
    }
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
    public void setAddressOne(String addressOne) {
        this.addressOne = addressOne;
    }
    public void setAddressTwo(String addressTwo) {
        this.addressTwo = addressTwo;
    }
    public void setCity(String city) {
        this.city = city;
    }
    public void setState(String state) {
        this.state = state;
    }
    public void setPostalCode(String postalCode) {
        this.postalCode = postalCode;
    }
}

Legacy Thinking

The main cause of this pattern sticking so long is just plain 'ol legacy thinking. Ask anyone why we do it and they will say, "what if we need to change the behavior of the getter? we can't expose our properties."

My challenge, give that person a "bean" with some behavior in the get* method. Watch them flip out. "What are you doing putting logic in a bean?!?!"

The original premise doesn't stand if you are not allowed to put logic in a bean. And if that's the case....... the class should really look like this:

public class CartUser {
    public String lastName;
    public String firstName;
    public String addressOne;
    public String addressTwo;
    public String city;
    public String state;
    public String postalCode;
}

It's just a data structure. It has no behavior.

Now, obviously, you don't want to do this to all of your POJOs. But, as a DTO or as a parameter object, why the heck not?
Besides, it's considered bad form to put logic in those types of objects anyway.

We're stuck blindly on an old paradigm. Dave Thomas described this in his Angry Monkeys keynote speech.

Fluent Interfaces

I know you're not gonna get away with the above (unless you are the team lead). And often, you still need the getters (passing an object to a framework or a JSP) As a compromise, let me suggest using a Fluent   Interface for your object.

I also recommend supporting method chaining when appropriate. It can make for more readable/concise code. The bean won't be any smaller for it, it just makes consumption easier.

For example, compare the following two methods:


public CartUser buildUser(){
  CartUser user = new CartUser();
  user.setFirstName("Paul");
  user.setLastName("Davis");
  user.setAddressOne("2012 16th Street");
  user.setAddressTwo("");
  user.setCity("San Francisco");
  user.setState("CA");
  user.setPostalCode("94103");
  return user;
}

public CartUser buildUser(){
  return new CartUser()
     .setFirstName("Paul")
     .setLastName("Davis")
     .setAddressOne("2012 16th Street")
     .setAddressTwo("")
     .setCity("San Francisco")
     .setState("CA")
     .setPostalCode("94103");
}

BTW, feel free to send beer to that address :-)

Truth is, we are very used to seeing the first form and it's not a huge deal. However, by adding return this; to the bottom of each setter (or better yet, make your IDE do it for you) we can add just a little more maintainability to the code. As our code bases get bigger, every little bit helps. Leaving the getters still makes it easy for frameworks/JSPs/whatever to consume the objects. Bonus points because our code is more easily read by non-programmers.
It's even better written as:


public CartUser buildUser(){
    return new CartUser()
        .withFirstName("Paul")
        .withLastName("Davis")
        .withAddressOne("2012 16th Street")
        .withAddressTwo("")
        .withCity("San Francisco")
        .withState("CA")
        .withPostalCode("94103");
}

Even your manager can read that.

Conclusion

Before blindly accepting common development patterns, make sure they are helping and not hindering.

The goal of all of this is to make development better, for us all. Don't forget, your code must be written for two different target audiences: the machine and other developers.

Author

by: Paul E Davis
Tech Tags:


Sponsors:

About willCode4Beer