Andrei Solntsev
11 Apr 2011

Keep API simple

Antud lugu on saadaval ainult inglise keeles

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

A bank built in 8 months - licence confirmed, deadline met

Build or buy? When the client came to us, that question was still open. We recommended building. No vendor lock-in, no paying for features you'll never use, no waiting on someone else's roadmap. A codebase you own. A product you control. They agreed. That put more weight on us - and we were fine with that.

The Codeborne Christmas beer brewing diaries

It was a sunny day in September. Quite warm for that time of year. We were sitting with my colleague Tiit on the roof terrace in the Codeborne office as we do every now and then. I ask him for advice on occasion - after all, what are the more experienced colleagues good for otherwise?