Try this. The initial reflection is certainly expensive, but if you're going to use it many many times, which I think you will, this is most certainly a better solution what what you're proposing. I don't like using reflection, but I find myself using it when I don't like the alternative to reflection. I do think that this will save your team a lot of headache, but you must pass the name of the method (in lowercase).
In other words, rather than pass "name", you would pass "fullname" because the name of the get method is "getFullName()".
Map<String, Method> methodMapping = null;
public Object getNode(String name) {
Map<String, Method> methods = getMethodMapping(contact.getClass());
return methods.get(name).invoke(contact);
}
public Map<String, Method> getMethodMapping(Class<?> contact) {
if(methodMapping == null) {
Map<String, Method> mapping = new HashMap<String, Method>();
Method[] methods = contact.getDeclaredMethods();
for(Method method : methods) {
if(method.getParameterTypes().length() == 0) {
if(method.getName().startsWith("get")) {
mapping.put(method.getName().substring(3).toLower(), method);
} else if (method.getName().startsWith("is"))) {
mapping.put(method.getName().substring(2).toLower(), method);
}
}
}
methodMapping = mapping;
}
return methodMapping;
}
If you need to access data contained within members of contact, you might consider building a wrapper class for contact which has all methods to access any information required. This would also be useful for guaranteeing that the names of the access fields will always remain the same (I.e. if wrapper class has getFullName() and you call with fullname, it will always work even if contact's getFullName() has been renamed -- it would cause compilation error before it would let you do that).
public class ContactWrapper {
private Contact contact;
public ContactWrapper(Contact contact) {
this.contact = contact;
}
public String getFullName() {
return contact.getFullName();
}
...
}
This solution has saved me several times, namely when I wanted to have a single data representation to use in jsf datatables and when that data needed to be exported into a report using jasper (which doesn't handle complicated object accessors well in my experience).
Why does it have to be a database? Why not just have a generic "data sink" and write streams of data to it. Plugins are responsible for serializing and deserializing their own data, and you provide a simple read/write facility. They give you a reference to a message stream, then you write it and hand them back a GUID (or some other token) to identify the data.
Best Answer
This is a dumb, stringly typed data structure. It is a:
This makes it difficult to create, traverse, and could very well get you into code that looks like:
And guess what... you've got a null pointer exception that was thrown on that line.
Setting up the structure isn't much fun either. That this is a rawish data structure means that you have to do all of the adds and deletes by hand working on the interfaces (that can be rather deep) rather than working with a Constructor or Builder.
You've linked the representation that is passed around in your code to the representation of the file. When one needs to change, the other needs to change - and possibly in multiple locations. Think of the joys you will have when you have an xml or json or yaml data file behind it and need to go through and change things.
This all in all is a bad idea.
A better idea would be to have one or more classes that exposes the logic of fetching the data. You have a
Structure
. It exposes aget(String bar, String qux)
and returns back aList
. This allows you to isolate the traversal of the structure (if it is that) in private fields so that it can change as needed. It also exposes anadd(String bar, String qux, LocalDate date)
which allows you to clearly isolate the logic for building it in a testable way.You may find that you want other questions of that data that you want answered. Maybe you want a
isInRange(...)
orcontains(...)
or something else that you ask. Failing to have this as a nice class with corresponding methods to answer those questions, you will find that you either have a static method in a helper class (this is beginning to smell) that acts on that data.That looks as ugly as it is. Don't do that.
If you don't do that, you will have a copy of that logic each time you use it which is not at all DRY.
Either way, working on raw data structures like this is undesirable.