Давайте сегодня вспомним о старой доброй статье "Эффект последней строки", написанной более 5 лет назад. Время идёт, но ничего не меняется. В этом нет ничего удивительного. Copy-Paste всё также жесток и беспощаден. Однако, за годы у нашего блога появилось много новых читателей, которые могут быть не знакомы с упомянутой статьёй. Так что сейчас будет минутка воспоминаний и немного дополнений.
Сегодня я встретил упоминание статьи "Эффект последней строки" в твите от Jason Turner. Как видим, тема Copy-Paste жива и продолжает вызывать обсуждения. Поэтому я решил, что стоит стряхнуть пыль с этой публикации и напомнить о ней. Уверен, многие не читали эту старую статью или забыли о ней. А ведь она весьма интересна и забавна.
Суть в следующем. Люди тяготеют допустить ошибку в конце однообразных операций, так как теряют внимание. Одним из таких действий является написание кода с помощью Copy-Paste.
Если требуется написать фрагмент кода, состоящего из однотипных блоков, то люди предпочитают копировать блок и вносить в него правки. Так вот я заметил, что с наибольшей вероятностью ошибка будет сделана в последнем блоке.
Именно эта закономерность с соответствующими примерами и описана в статье "Эффект последней строки".
Не хочется здесь пересказывать содержание этой статьи. Поэтому если вы с ней не знакомы, прошу сейчас перейти по ссылке и прочитать её.
К сожалению, статья писалась, когда анализатор PVS-Studio ещё не умел анализировать C# и Java проекты. Поэтому все приведённые в статье ошибки относятся к языку C или C++.
Сейчас я бы смог привести аналогичные примеры, относящиеся и к другим языкам. Проблематика Copy-Paste универсальна и проявляет везде себя одинаково. Чтобы не быть голословным, приведу по одному примеру для C# и Java.
Пример ошибки, найденной нами в C# проекте AWS SDK for .NET.
if (
(this.token == JsonToken.ObjectEnd ||
this.token == JsonToken.ArrayEnd ||
this.token == JsonToken.String || // <=
this.token == JsonToken.Boolean ||
this.token == JsonToken.Double ||
this.token == JsonToken.Int ||
this.token == JsonToken.UInt ||
this.token == JsonToken.Long ||
this.token == JsonToken.ULong ||
this.token == JsonToken.Null ||
this.token == JsonToken.String // <=
))
{
....
}
Пример ошибки, найденной нами в Java проекте Elasticsearch.
for (int i = 0; i < values.length; i++) {
if (values[i] == null) continue;
if (values[i] instanceof String) continue;
if (values[i] instanceof Text) continue;
if (values[i] instanceof Long) continue;
if (values[i] instanceof Integer) continue;
if (values[i] instanceof Short) continue;
if (values[i] instanceof Byte) continue;
if (values[i] instanceof Double) continue;
if (values[i] instanceof Float) continue;
if (values[i] instanceof Boolean) continue; // <=
if (values[i] instanceof Boolean) continue; // <=
throw new IllegalArgumentException(....);
}
Как видите, везде всё одно и то же :). Удивляться этому не приходится, так как опечатки и проблемы неаккуратного Copy-Paste почти не зависят от выбранного языка.
Кстати, тем, кто хочет глубже погрузиться в обсуждаемую тему, предлагаю следующую исследовательскую статью, написанную уже коллективом авторов: "Объяснение эффекта последней строки".
Что теперь делать с этим знанием? Хороший вопрос. У меня есть три соображения: