Программируйте осознанно
(В оригинале - Coding with Reason)
Попытка определить корректность кода вручную приводит к формальной проверке, что получается и дольше, чем само написание кода, и с большей вероятностью ошибки в самой проверке, чем в коде. Автоматические инструменты с этой точки зрения более предпочтительны, но их использование не всегда возможно. Здесь будет описано нечто среднее – полуформальное определение правильности.
Основная идея – вдумчиво разделить код на короткие секции – от одной строки (например, вызов функции) до блоков из десятка строк, и аргументировать их правильность. Аргументы должны быть достаточны для убеждения вашего коллеги, играющего роль «адвоката дьявола».
Секции должны подбираться так, чтобы в конце каждой из них «состояние программы» (значение всех «живых» на данный момент объектов) удовлетворяло легко формулируемому критерию, а функциональность этой секции бы легко описывалась одной задачей. Такие состояния конечных точек обобщают концепции вроде пред- и пост-условий для функций и инвариант (тела) для циклов и классов. Старайтесь, чтобы секции были как можно более независимыми друг от друга.
Большинство широко известных «хороших» практик программирования (хотя им не так часто следуют) облегчают построение аргументации. Уже даже от того, что вы задумаетесь об аргументации правильности вашего кода, вы начнете думать в направлении лучшего стиля и структуры кода. И конечно же, соблюдение большинства этих практик можно проверить при помощи статических анализаторов кода:
- Избегайте операторов goto, поскольку они делают удаленные друг от друга секции сильно связанными.
- Избегайте изменяемых глобальных переменных, поскольку они делают секции, их использующие, зависимыми.
- Каждая переменная должна иметь минимально возможную область видимости. Например, локальный объект может создаваться непосредственно перед его использованием.
- Делайте объекты неизменяемыми там, где только это возможно.
- Делайте код легко читаемым используя пробелы, как по горизонтали, так и по вертикали. Например, выравнивайте зависимые структуры и разделяйте секции пустыми строками.
- Делайте код самодокументируемым, выбирая «говорящие» имена, при этом не слишком длинные.
- Если вам нужна вложенная секция, сделайте ее функцией.
- Делайте функции короткими и сфокусированными на единственной задаче. Старое ограничение на 24 строки все еще актуально. Хотя разрешение экрана сильно выросло, способность человека воспринимать информацию осталась той же.
- Функции должны иметь ограниченное число параметров (скажем, максимум четыре). Заметьте, это не накладывает ограничение на объем данных, которые можно передать. Просто сгруппируйте данные в объект и передавайте его, заодно обеспечив когерентность и связность этих данных.
- Каждый модуль должен иметь минимально возможный интерфейс. Меньше коммуникаций – меньше потребуется аргументов для доказательства правильности. К примеру, “getter”, возвращающий внутренние данные объекта – потенциальный источник проблем. Вместо запрашивания у объекта данных, чтобы потом что-то с ней сделать, пусть эту работу со своей информацией делает сам объект. Другими словами, инкапсуляция – лучшее средство для «сужения» интерфейса.
- Для защиты инвариантности объектов “setter”-методы не должны поощряться, поскольку они легко позволяют нарушить инвариантность.
Кроме доказательства правильности кода, его обсуждение даст вам лучшее его понимание. Развитие проницательности выгодно для всех.
Автор оригинала - Yechiel Kimchi