Pull to refresh

Keep API simple

Reading time2 min
Views747
Я хочу рассказать об одном случае, когда нам удалось придумать простой API, когда поначалу задача казалось сложной.


Недавно мы получили задачу. Нам надо было логировать каждое действие пользователя, которое он может совершать на нашем веб-сайте. Другими словами, нам нужно было создать какой-то класс (API), который легко можно было бы использовать практически во всех контроллерах в нашей системе. Сложности добавляло то, что в зависимости от действия надо логировать разные дополнительные параметры.

Вместе с этим заданием нам досталось наполовину готовое решение, сделанное некими разработчиками.


Первое решение


Мы не будем сейчас рассматривать, как сделано логирование внутри — нам интересно посмотреть, насколько удобно его использовать снаружи (то есть как раз API). Вот как выглядел типичный код контроллера, в котором нужно залогировать действие пользователя:
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);
}


Наверное, очевидно, как работает LogUtil.getParamsWithFieldNames. Получив объект «user» и список строк, он вызывает соответствующие геттеры: user.getPersonCode(), user.getUserName() и т.д.

Прелестно


Ну как, изящно?
Посмотрите, какое продуманное, универсальное решение! Оно не зависит от конкретного класса, будь то User, Client, Customer или что-то ещё. Просто скормите этот объект и список строк классу LogUtil, и он сам добавит в лог все эти значения. О да, это умное API!

Но


Только вот знаете что? Нужна вам эта умность, как собаке пятая нога!
Остановитесь на секунду и подумайте: нельзя ли это сделать проще? Конечно можно!

Вот зачем нам тут использовать reflection? Зачем терять преимущество, которое даёт компилятор — он ведь может сразу найти ошибку, если вы вдруг ошиблись в написании геттера? Зачем надо заморачиваться с обработкой исключений? Почему просто не использовать геттеры?

И таки да


В итоге решение получилось проще, короче и надёжнее. Вот как теперь выглядит типичный код контроллера:
  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);




Вот так. Настолько просто, насколько это возможно.
Как и завещал старина Эйнштейн.

Tags:
Hubs:
Total votes 19: ↑12 and ↓7+5
Comments18

Articles