Saturday, 5 February 2011

Simplify your Beans

One of the boring grunty tasks that you always seems to end up having to do is to implement a toString() method on your plain old Java beans, data transfer objects or value objects. That’s because at some point during your development cycle you’ll need to know the values returned by each of your ‘getter’ methods in order to visually verify its state.

Now you could implement toString() on each of your beans, but that’s a little time consuming. This utility, which prefers aggregation over inheritance, takes any object and using reflection, returns a ready made debug string.
public final class ToStringHelper {

    private ToStringHelper() {
        // Blank
    }

    /**
     * 
     * This method will interrogate the bean, find all the getters convert their names and
     * values into a string for debugging
     * 
     * @param obj
     *            The bean to interrogate
     * @return A String of all the values
     */
    public static String toString(Object obj) {

        StringBuilder sb = new StringBuilder("[Bean : ");
        sb.append(obj.getClass().getSimpleName());

        sb.append("  {");

        Method[] ms = obj.getClass().getMethods();

        for (Method method : ms) {
            String name = method.getName();

            if ((name.startsWith("is")) ||
                ((name.startsWith("get") &&
                (!name.equals("getClass"))))) {
                try {
                    sb.append(name);

                    Object data = method.invoke(obj, (Object[]) null);
                    if (data != null) {
                        sb.append("() = \"");
                        sb.append(data);
                        sb.append("\"");
                    } else {
                        sb.append(" = null");
                    }

                    sb.append(" | ");

                } catch (Exception e) {
                    String str = "Problem calling getter: " + name + " on object: "
                            + obj.getClass().getName() + "\nMessage: " + e.getMessage();

                    LogFactory.getLog(obj.getClass()).debug(str);
                    throw new SystemException(str, e);
                }
            }
        }

        sb.append("}]\n");
        return sb.toString();
    }
}
The helper class above takes any object and trawls its internals looking for methods names that begin with “get” or “is”. When it finds one, it’ll invoke the method and add the return value to a StringBuildier instance. Once all the ‘getter’ methods have been called then the StringBuilder instance is returned to the client.

To demonstrate that this works, we’ll create an instance of a TestBean that we’ll define as:
public class TestBean {

    private final String attribute1;
    private final String attribute2;
    private final String attribute3;
    private final String attribute4;
    private final boolean flag;

    public TestBean(String attribute1, String attribute2, String attribute3,
                    String attribute4, boolean flag) {
        this.attribute1 = attribute1;
        this.attribute2 = attribute2;
        this.attribute3 = attribute3;
        this.attribute4 = attribute4;
        this.flag = flag;
    }

    public String getAttribute1() {
        return attribute1;
    }

    public String getAttribute2() {
        return attribute2;
    }

    public String getAttribute3() {
        return attribute3;
    }

    public String getAttribute4() {
        return attribute4;
    }

    public boolean isFlag() {
        return flag;
    }
    
    @Override 
    public String toString() {
        return ToStringHelper.toString(this);
    }    
}
Then we can call its toString() using the following code:
TestBean instance = new TestBean("attribute1", "attribute2", "attribute3", "attribute4", true);
System.out.println(instance.toString());
The result will be a human readable, meaningful debug string.
[Bean : TestBean  {getAttribute1() = "attribute1" | getAttribute2() = "attribute2" | getAttribute3() = "attribute3" | getAttribute4() = "attribute4" | isFlag() = "true" | }]
This obviously works best for getter methods that return a String or primitive type that can automatically be converted to a string using the StringBuilder.append(...) method. It does work then the ‘getter’ method returns a more complex object, so long that that object also implements toString().

No comments: