Andrei Solntsev
11 Apr 2011

Keep API simple

I want to share a success story of designing a simple API, when the problem seemed to be complex at first glance.

User being happy illustration

Recently we got a task. We had to log every action that user does on the web. In other words, we would need a simple class (API) which could be easily used from almost all the controllers in our web. Additionally, we should log different parameters depending on action.We also have got a draft solution for this task from unknown developers. We are not going to look inside logging; we are interested in how easy is to use logging (it’s API). Below you can see a typical controller code for logging user action:


private void logUserAction(User user) throws Exception {
  UserActionModel model = new UserActionModel();
  model.setAction("Buy a ticket");


  List<String> values = new ArrayList<String>();
  values.add("PersonCode");
  values.add("UserName");
  values.add("ContactInformation.EmailAddress");
  values.add("ContactInformation.Language");


  model.setParams(LogUtil.getParamsWithFieldNames(user, values));


  LogUtil.log(model);
}

Probably you guess how LogUtil.getParamsWithFieldNames works. Given a “user” object and list of Strings, it calls corresponding getters: user.getPersonCode(), user.getUserName() etc.

It’s it nice?

Look what a mature, multi-purpose solution! It doesn’t depend on concrete class - it could be User, Client, Customer or whatever else. You can pass any object and list of field names, and this universal LogUtil can log all these fields. Yeah, that’s a smart API!

But

But you know what? You just don’t need this smartness.Stop for a while and think: couldn’t it done easier? Of course it could!Why do you need reflection? Why loose compile-time check of getter names? Why bother with handling reflection (checked) exceptions? Dude, just use getters!

Finally

The final solution didn’t use reflection and was not smart - it was simple.The following is the typical controller code for logging user action:


Action action = new Action("Buy a ticket")
    .add("PersonCode", user.getPersonCode())
    .add("PersonName", user.getPersonName())
    .add("EmailAddress", user.getContactInformation().getEmailAddress())
    .add("Language", user.getContactInformation().getLanguage());


  LogUtil.log(action);

As simple as possible.

Archived comments

Ivo 2011-10-19T12:24:13.531Z

Why would you implement this programmatically anyway? IMHO this is a clear case for aspect usage …

Our recent stories