Давайте поможем инженерам из Amazon с их SDK 2.0. Разберем наиболее серьезную ошибку – частичную инициализацию объекта.

В прошлой заметке я упоминал, как создаются объекты в SDK. Если коротко, у классов скрыты конструкторы, и нужно пользоваться билдером. Получается что-то вроде такого:

GetObjectRequest request = GetObjectRequest.builder()
  .bucket("acme-releases")
  .key("path/to/file.txt")
  .ifEtagMatches("....")
  .ifModifiedSince("...")
  .build()

Билдер не знает, какие из параметров обязательны, а какие нет. В примере выше код работает. Если убрать вызов .bucket или .key, все скомпилируется, но при запуске получим:

Execution error (IllegalArgumentException)
at ....xml.internal.marshall.SimpleTypePathMarshaller

Parameter 'Bucket' must not be null

Обратите внимание, что ошибка приехала из какого-то XML-маршаллера, хотя никакого XML и тем более маршаллизации здесь нет. Тело пустое, и просто составляется URL.

Подчеркну: программистам из Амазона вполне ОК, что объект инициирован частично. Спрашивается, что можно сделать с объектом GetObjectRequest, если у него не заполнен бакет? Ничего. Зачем тогда позволять такую ситуацию?

Как они вообще представляют работу со своим SDK? Пользователь садится и перебором проверяет, какие поля нужны, а какие нет? Ладно я знаю, что бакет и ключ необходимы, но ведь кто-то не знает. И узнает он только когда бахнет прод.

Проблема решается просто. Все поля объекта делятся на обязательные и нет – по аналогии с аргументами args и kwargs в Питоне. Обязательные поля потому так и называются, что без них невозможно дернуть конструктор или порождающий статичный метод. В нашем случае обязательны бакет и ключ. С ними код становится таким:

GetObjectRequest request = new GetObjectRequest(
  "acme-releases", "path/to/file"
).withEtag("...")
  .withModifiedSince("...")

либо то же самое с билдером:

GetObjectRequest request = GetObjectRequest.builder(
  "acme-releases", "path/to/file")
.etag("...")
  .modifiedSince("...")
  .build()

То есть хоть разбейся в лепешку, но обязательные параметры передай.

Ну? Что мешало так сделать? Здесь даже паттерны сохранены, чтобы не пострадало чувство прекрасного.

Заметим, что я не говорю отсебятину: все это сказано в книге Effective Java авторства Джошуа Блоха. Он так и пишет: используйте неизменяемые объекты, не допускайте частичной инициализации, требуйте обязательные поля сразу – не надейтесь, что кто-то заполнит из позже. Кумир джавистов говорит, как делать правильно. Почему в Амазоне решили, что сами с усами?

Впрочем, пока я писал это, подумал – может, все гораздо проще? Может быть, за SDK сажают мидлов и стажеров, пока они без задач? Скажем, наняли стажера, а у тимлида релиз, погружать человека некогда, поэтому его сажают за SDK. Вполне похоже на правду.

У нас так было в Датаарте: пока человек без работы, его сажали за всякий внутренний хлам. Форму заказа пиццы, аукцион парковочных мест, каталог сотрудников. Все это было крайне низкого качества, потому что шло через десятки джунов и мидлов без какого-либо контроля качества.

Может и в Амазоне такой же порядок? Кто знает, расскажите. Потому что чем иначе объяснить такое качество SDK – я не знаю.