Предыстория:
... в одном проекте (как всегда, я - о НЕтривиальныхЪ эмбеддедЪ системахЪ
) понадобилось написать свой "класс" двухсвязного списка.
Написали.
Достаточно долго Использовали.
Всё было хорошо.
...
Пока, однажды, не уволился автор, написавший этот "класс"...
...А на его место, "на поддержку и сопровождение", не пришёл молодой программист.
Но - смышлёный и способный (без сарказма говорю!).
...
Далее - пойдёт не в духе "работает? НЕ ТРОГАЙ!".
Короче.
Первоначальным толчком к "лазанью и копанию в коде", послужила инициатива по "оптимизации".
Кому-то запала идея, что можно "ещё чуток" ускорить работу кода, использующего упомянутые двухсвязные списки, если принять во внимание, что, в большинстве случаев, не надо "всякие там предусловия проверять". Типа - программисты же - не совсем дурачки и не будут в качестве "this" NULL в "метод" списка подсовывать... Или удалять элемент NULL из списка...
...Или удалять уже ранее удалённый элемент...
Ведь - правда же?
...
Короче, ПРЕДУСЛОВИЯ были в "методах" этого "класса" двухсвязного списка пересмотрены ОЧЕНЬ ТЩАТЕЛЬНО.
... И честно проверены не одной парой глаз...
... и "на пальцах" "мозговым штурмом" совместным обсуждением утверждена правильность логики работы каждого из "методов"...
А тесты стали выдавать "непрохождение".
Что вызвало некоторое замешательство у народа. Типа, "где там можно было напортачить"?
Естественно, что головы всех работников повернулись одновременно в сторону нового сотрудника и все дружно и хором спросили: "Ты шо-то менял в предусловиях и основной логике методов?"
Тот, естественно (и - ЧЕСТНО!) ответил: "НЕТ!"
Не поверили.
Стали в шесть пар глаз на проекционном экране смотреть и, чуть ли, не пальцем по строчкам водить и вслух хором прочитывать строки кода и, останавливаясь, переглядываясь и хором утверждаясь в верности понятого, убеждаться, что "всё правильно".
Далее, не смотря на то, что diff, В ЭТИХ ЧАСТЯХ МЕТОДОВ, никаких расхождений не нашёл (а в возвращаемых результатах методов И ТАК ВСЁ ОЧЕВИДНО(sic!!!)), прогнали сборку и запуск тестов.
Тесты не прошли.
Все хором почесали затылки и уставились на список соответствия ожидаемых и фактических результатов в тестах.
И тут меня, как "торкнуло". Я смотрю на код и спрашиваю: "А почему ты возвращаемые значения (логического типа) поменял в некоторых "методах" на противоположные?"
Человек отвечает: "Дык - неправильно же по смыслу было!"
"- Почему?! Объясняй."
И тут выяснилось полное соответствие одному из законов Мерфи. То есть, когда уже кажется, что всё разжёвано до конца и ничего не может быть понято НЕ ВЕРНО, обязательно найдётся хотя бы один и работников, кто поймёт всё совершенно не так.
Проиллюстрирую на примере операций добавления и удаления элемента в/из списка.
Все операции возвращали булевское значение, как успех/неуспех операции. То есть, если элемент успешно добавлялся/удалялся в/из списка - истина, если что-то пошло не так - ложь.
Что могло помешать успешности? Понятно, что проверялись аргументы (список и указатель на элемент - на непустоту). Кроме того, была ещё проверка на уже содержание данного элемента в данном списке. Это было "место изменения сознания" номер раз.
При удалении элемента - так же - проверялись аргументы и НАЛИЧИЕ этого элемента в данном списке. Это было место номер два.
В указанных двух местах человек поменял значение возвращаемого логического значения на противоположное.
То есть, автор первоначального варианта методов списка и молодой программист подразумевали РАЗНЫЙ СМЫСЛ УСПЕШНОСТИ ВЫПОЛНЕНИЯ ОПЕРАЦИЙ:
На добавлении элемента в список:
Автор: элемент, который пытаются добавить в список - уже содержится в нём. Значит, была какая-то ошибка во внешнем коде, потому, что такого случиться не должно было (там есть метод для проверки принадлежности элементам списка, его нужно было использовать) - ВОЗВРАТИТЬ "ЛОЖЬ"!
Молодой программист: элемент, который пытаются добавить в список - уже содержится в нём - ВОЗВРАТИТЬ "ИСТИНА"!
На удалении элемента из списка:
Автор: элемент, который пытаются удалить из списка, не содержится в нём. Значит, была какая-то ошибка во внешнем коде, потому, что такого случиться не должно было (там есть метод для проверки принадлежности элементам списка, его нужно было использовать) - ВОЗВРАТИТЬ "ЛОЖЬ"!
Молодой программист: элемент, который пытаются удалить из списка, не содержится в нём. Значит, работа уже сделана - ВОЗВРАТИТЬ "ИСТИНА"!
То есть Автор списка подразумевал "расширенное состояние" не только списка, а молодой программист размышлял об успешности исполнения конкретной операции для данного элемента.
А - действительно, КУДА включать проверку того, содержит ли данный список данный объект?
Вызывать снаружи?
Или - организовывать в виде части предусловия внутри метода списка?
Или такой выбор - таки - будет частью проектного решения для конкретной системы?
Как вы думаете?
(Меня не интересует конкретность реализаций в разных системах и библиотеках, используемых вами в ваших программах! Меня интересует, как бы ВЫ решили эту "маленькую и незначительную проблемку"? )