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

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?

“Backing up” a good product owner

One of the key players in most successful agile projects is a product owner, at least in Codeborne’s practice. Our practice stretches for more than 15 years, during which we have successfully delivered over 100 projects.