Friday, October 8, 2010

Slacker Duck Typing for Properties in Java

So the whole "walking, quacking, and giving advice on accident insurance" thing that ducks are know to do is not only a "cool" thing but a sanity thing. Once you have written your Nth DTO to Domain Object transformer, you get the feeling that you need some kind of framework to solve this problem. Why do you even need transformers? Well glad you asked. Also, what about that title, how does a transformer relate to Duck Typing? I'll get to that RSN.

Why Transformers?
Well it turns out that sometimes in Java land, where typing is static and strict, you need to deal with an ungainly domain object in sane, little pieces. Imagine a "roll-up" inheritance scenario where one DB row is N classes (with a type selector column). Imagine you have logic that wants to deal with specific types. What is an easy way to get that data from the blob to the type of interest?

Slacker Duck Typing
Given our problem statement, here is my suggestion for using BeanUtils to implement what I like to call Slacker Duck Typing. Let's assume we have a blobby type called Thingy, which for some reason is an instance of roll-up inheritance and is declared like this (I have omitted the getter/setters for each class to save space):
public class Thingy {
private String type;
private String name;
private String[] dna;
private Integer numOfCores;

public Thingy(String type, String name, Integer numOfCores, String[] dna) {
this.type = type;
this.name = name;
this.numOfCores = numOfCores;
this.dna = dna;
}
// getter/setter pairs were not put here to save space.
}
Further assume we have these two types: Human and Robot:
    public static class Human {
private String name;
private String[] dna;
// getter/setter pairs were not put here to save space.
}

public static class Robot {
private String name;
private Integer numOfCores;
// getter/setter pairs were not put here to save space.
}

Now, imagine that you need to unpack the blob class and you want to unpack it into the correct real type. You'll notice I did not use inheritance because that would be wrong. LSP would be violated if I asked a human for their numOfCores or a robot for their dna. Also, Java won't let me narrow the scope of the property methods of Thingy.

What we need here is Duck Typing, we need to be able to assume if a class has some behavior (methods) then we should not care about the type and just invoke the methods. In our case, Human has a name and it has dna which not-so-coincidently so does Thingy. Hmmm, so let's follow the rule that if the types cannot be related at least let the property names be the same.

Now, here's the slacker part shown in the main:

public static void main (String[] args) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {

Thingy thingy = new Thingy("H", "Carbon Unit 100", null, new String[]{"CTGA", "CTTG", "AGTC"});

Map humanThingy = describe(thingy);

Map robotThingy = describe(new Thingy("R", "Joe", 10, null));

Human human = new Human();
populate(human, humanThingy);

String humanString = ToStringBuilder.reflectionToString(human).toString();
System.out.print(String.format("Humans are like this --> %s\n", humanString));

Robot robot = new Robot();
populate(robot, robotThingy);

String robotString = ToStringBuilder.reflectionToString(robot).toString();
System.out.print(String.format("Robots are like that --> %s\n", robotString));
}

Did you see how lazy I was? I simply used:

import static org.apache.commons.beanutils.BeanUtils.*;

to empower my program to extract property name-value pairs from one type and apply them to the other type. The best part for me is this, no one cares about types in this process because the transform logic, now deputized with the powers of BeanUtils, will happily get all the data in one object and apply it to another. If.... if you follow naming conventions (type conventions not required though I did use them in this example).

Now, this relies on BeanUtils not being too fussy about the types of data pushed to and pulled from the Map, but if it ever decided to do something that broke this, it is pretty easy to replace this code with your own.

So we see by being lazy and conventional (the names) we can do more work with less effort. At this point I must thank JBrains for making me aware of the power of BeanUtils and I'll thank some blobby code I had to deal with for inspiring me to think of using BeanUtils this way.

No comments:

Post a Comment