четверг, 18 июня 2015 г.

Про дублирование кода

Я не понимаю людей, которые всерьёз оправдывают дублирование кода. Ну не понимаю и всё. Причём, говорят они вовсе не о мелких часто повторяемых конструкциях, а о вполне больших и сложных кусках логики на несколько сотен строк кода!
В свою защиту они приводят следующие аргументы:
  • "Вот когда мне понадобится третий раз скопировать код, тогда и вынесу общую часть в отдельный модуль".
  • "Эти куски кода лежат в разных проектах, поэтому чтобы выделить общую часть нужно создавать общую зависимость".
По-моему, всё это полнейший бред. Единственное логичное объяснение, почему люди не хотят бороться с дублированием кода, – это элементарная лень и непонимание последствий. А последствия дублирования кода часто бывают катастрофическими.
Обычно проявляется это следующим образом: в программе обнаруживается критический баг, причиной которого является допущенная ошибка в этом самом куске кода. Это баг назначают не на автора кода, а на другого члена команды. Этот член команды понятия не имеет, что где-то в другом месте проекта есть точно такой же кусок кода, и соответственно исправляет ошибку только в одном месте, но не исправляет в другом. Итог: баг закрыт, но только для определённых сценариев использования программы, когда вызывается именно этот участок кода. Но другие сценарии, когда вызывается другой, скопированный участок кода, по-прежнему будут работать ошибочно.
Другой вариант развития событий – член команды (который не в теме копипасты кода) оказался более проницательным и всё-таки смог обнаружить, что у этого кода есть дубликат в другой части проекта. Соответственно, он выбирает одно из двух: либо исправляет ошибку в обоих местах (по-сути, оставляет дублирование), либо героически берётся выносить дублирующийся код в общий модуль. Но выносить дублирующийся код – это часто задача не из простых, потому что ты должен отлично понимать логику, которую писал автор, чтобы ничего не поломать. А если логика сложная, то ты потратишь уйму драгоценного времени для её понимания. Хорошо если автор этого кода ещё работает в проекте, и у него можно спросить, что же он там имел в виду. А если он уже давно уволился? Вот если бы автор кода сразу вынес код в общий модуль, то он бы сделал это гораздо быстрее, так как тогда был в теме и держал все детали в голове.
Дублирование кода – это способ выиграть полчаса времени при написании программы засчёт траты в будущем от одного часа до целого рабочего дня. Это может быть приемлемо для какого-нибудь прототипа на Питоне, который вы точно знаете, что он будет гарантированно выброшен, но совершенно неприемлемо для больших и сложных программ, которые будут жить, развиваться и поддерживаться следующие несколько лет.

среда, 17 июня 2015 г.

Методы, принимающие на вход Object

Возможно я буду уже 1000-ным (или 10000-ным) человеком, который это говорит, но я всё же повторю ещё раз.

Методы, у которых есть аргументы типа Object – это полнейший пиздец.

Примеры таких методов:
  • Object.equals(Object)
  • Objects.equals(Object, Object)
  • Collection.contains(Object)
  • Collection.remove(Object)
  • Map.containsKey(Object)
  • Map.containsValue(Object)
Почему это лютейший пиздец, я поясню на примере. Допустим, у вас в программе есть переменная типа Set<Integer> и где-то в коде есть проверка:
Integer x = ...;
if (set.contains(x)) {
  // Do something
}
Теперь в один прекрасный день вам нужно поменять тип множества, скажем, на Set<String>. Вы меняете, и... код компилируется! Но проверка теперь работает некорректно, т.к. вы проверяете, содержит ли ваше множество число, что всегда будет возвращать false. Вы сделали фатальную ошибку в программе, но компилятор вас об этом не предупредил!
Так жить нельзя. Java – это пример статического языка, который не является типобезопасным. Статически типизированный и нетипобезопасный язык – что это если не пиздец?
Есть ли решение у этой проблемы? Ну не знаю, equals(Object) навсегда вшит во все объекты. Так будет в Java 9, 10, 11 и в 20. Поменять сигнатуру метода невозможно, т.к. это нарушит обратную совместимость. Можно использовать typeclass-подход, который используется в functionaljava, но разве кто-нибудь так будет делать?
Остаётся надеяться на IDE и статические анализаторы, хотя это частичное решение, не полное.