Итак, перерыв кучу святых войн (от англ. holly wars) о том, что же лучше, меня вдруг осенило, как же просто решить этот извечный вопрос
На самом деле нет ничего проще!
Два простых правила:
- Табы для блочных отступов;
- Пробелы — для декорации.
Ещё не уловили суть?
Так вот, табы ставим там, где происходит логический блочный отступ, а когда нужно сместить текст следующей строки под какой-то конкретный символ предыдущей (посимвольное декорирование), то ставим нужное количество пробелов, но уже после табов, которые сдвигают сам блок.
Парочка примеров
Нет ничего нагляднее живых примеров. По-этому я возьму достаточно специфичный кусочек кода (см. исходный файл), который написан в нестандартном стиле автора пакетного менеджера npm для node.js, итак — JavaScript код:
function localLink (target, where, context, cb) { ---→log.verbose("localLink", target._id) ---→var jsonFile = path.resolve( npm.globalDir, target.name ---→···························, "package.json" ) ---→··, parent = context.parent ---→readJson(jsonFile, function (er, data) { ---→---→if (er || data._id === target._id) { ---→---→---→if (er) { ---→---→---→---→install( path.resolve(npm.globalDir, "..") ---→---→---→---→·······, target._id ---→---→---→---→·······, function (er) { ---→---→---→---→·········---→if (er) return cb(er, []) ---→---→---→---→·········---→thenLink() ---→---→---→---→·········} ---→---→---→---→·······) ---→---→---→} else thenLink() ---→---→---→function thenLink () { ---→---→---→---→npm.commands.link([target.name], function (er, d) { ---→---→---→---→---→log.silly("localLink", "back from link", [er, d]) ---→---→---→---→---→cb(er, [resultList(target, where, parent && parent._id)]) ---→---→---→---→}) ---→---→---→} ---→---→} else { ---→---→---→log.verbose("localLink", "install locally (no link)", target._id) ---→---→---→installOne_(target, where, context, cb) ---→---→} ---→}) }Табуляция здесь выделена как ---→ (предполагается, что она шириной 4 пробела), а пробельный отступ как ·. Следует заметить, что я очень удачно выбрал пример, потому как он вскрывает и недостаток такого приёма [1]. Если вы обратили внимание, в коде есть место где идут табы, пробельное декорирование и дальше снова табы для блочного отступа функции, переданной как аргумент другой функции.
Недостаток
Многие редакторы кода воспринимают таб как декоратор [2], и иной раз учитывают количество символов (включая пробелы) перед последним табом, и исходя из этого количества — вычитают от последнего таба длину, чтобы в итоге выглядело так, как если бы вместо символов от начала строки до этого места стояли одни табы (надеюсь понятно сформулировал). В итоге при длине таба в 4 пробела мы можем получить отступ у этой вложенной функции в 3 пробела, но как мне кажется это не сильно критично.
Пример с шириной таба в 2 пробела
Итак, а теперь для наглядности этого способа приведу этот же пример, но с шириной в 2 табуляции:
function localLink (target, where, context, cb) { -→log.verbose("localLink", target._id) -→var jsonFile = path.resolve( npm.globalDir, target.name -→···························, "package.json" ) -→··, parent = context.parent -→readJson(jsonFile, function (er, data) { -→-→if (er || data._id === target._id) { -→-→-→if (er) { -→-→-→-→install( path.resolve(npm.globalDir, "..") -→-→-→-→·······, target._id -→-→-→-→·······, function (er) { -→-→-→-→·········-→if (er) return cb(er, []) -→-→-→-→·········-→thenLink() -→-→-→-→·········} -→-→-→-→·······) -→-→-→} else thenLink() -→-→-→function thenLink () { -→-→-→-→npm.commands.link([target.name], function (er, d) { -→-→-→-→-→log.silly("localLink", "back from link", [er, d]) -→-→-→-→-→cb(er, [resultList(target, where, parent && parent._id)]) -→-→-→-→}) -→-→-→} -→-→} else { -→-→-→log.verbose("localLink", "install locally (no link)", target._id) -→-→-→installOne_(target, where, context, cb) -→-→} -→}) }Как видите, — декорирование сохранилось и код не рассыпался.
Супер наглядность!
А для полной наглядности, берём подготовленный мною код вот здесь, смотрим для начала в браузере (у меня в FireFox отступ таба по умолчанию аж 8 пробелов), потом открываем его в своём любимом редакторе, меняем ширину таба и любуемся. Напоминаю, что оригинальный код автора можно найти тут.
Ещё парочка примеров
Декорирование кода посимвольно везде и всюду, — это я бы сказал причуда для особых ценителей (за редкими исключениями, когда это имеет смысл [3]), но это серьёзная тенденция среди программистов, особенно мною замечено среди C++ кода, посему следующий пример будет на этом языке. Это пример из заголовочных файлов API библиотеки FMOD:
/* ---→'DSPConnection' API */ class DSPConnection { ··private: ---→DSPConnection();····/* Constructor made private so user cannot statically instance a DSPConnection class. ---→·······················Appropriate DSPConnection creation or retrieval function must be used. */ ··public: ---→FMOD_RESULT F_API getInput··············(DSP **input); ---→FMOD_RESULT F_API getOutput·············(DSP **output); ---→FMOD_RESULT F_API setMix················(float volume); ---→FMOD_RESULT F_API getMix················(float *volume); ---→FMOD_RESULT F_API setLevels·············(FMOD_SPEAKER speaker, float *levels, int numlevels); ---→FMOD_RESULT F_API getLevels·············(FMOD_SPEAKER speaker, float *levels, int numlevels); ---→// Userdata set/get. ---→FMOD_RESULT F_API setUserData···········(void *userdata); ---→FMOD_RESULT F_API getUserData···········(void **userdata); ---→FMOD_RESULT F_API getMemoryInfo·········(unsigned int memorybits, unsigned int event_memorybits, unsigned int *memoryused, FMOD_MEMORY_USAGE_DETAILS *memoryused_details); };В данном примере мы видим равномерно выравненные описания аргументов к функциям. Также здесь затронуто посимвольное декорирование комментариев в коде, что не менее важно! И хочу сообщить, что автор сделал всё за меня, он как и следовало, использовал табы для блочного отступа и пробелы для декорирования. Так же он поставил по 2 пробела перед private: и public: (обычно перед ними нет отступов и они находятся наравне с class), я решил так и оставить для демонстрации с сохранением стиля автора. В итоге код я не менял.
Также очень популярно использовать посимвольное декорирование для пар ключ-значение и имён переменных и их значений. Приведу пример от себя снова на языке JavaScript:
//декорированное объявление переменных var myVar············= 123 ··, variable·········= 'check' ··, myLovelyVariable·= 'yes!' ··, flag·············= false; //декорированный объект пар ключ-значение (хеш) { ---→keyFirst····················: 123, ---→myLovelyKey·················: 'yes!', ---→veryVeryVeryVeryLongKeyName·: 'ok', ---→oneMore·····················: true }Вот собственно и всё, на этом примеров по-моему достаточно, чтобы понять суть.
Итоги
Следует привести выжимку за и против применения такого метода, немного самокритики не повредит.
Аргументы за:
- Снимается вопрос об стандартизации отступа блоков и отдаётся дань личному предпочтению каждого программиста, один и тот же код, выглядит так, как удобно отдельно взятому человеку;
- Не нужно больше выбирать, табы или пробелы, вы используете их вместе;
- Вы используете табы и ваш код не разваливается и не разъезжается в зависимости редактора или просмотрщика кода;
- Табы легче набирать, во всяком случае быстрее чем 2 или даже 4 нажатия по пробелу, хотя плюсом это считать нельзя, потому как умные современные редакторы кода спокойно делают нужное количество пробелов по нажатию табуляции.
- Есть узкие места, которые были описаны, связанные с тенденциями редакторов и просмотрщиков применять к табам декоративные свойства, но во многих языках программирования такие ситуации отсутствуют;
- Требуется много внимания, чтобы не ошибиться и правильно расставить табы и пробелы, в некоторых случаях нужно подумать, перепроверить себя, хотя ответ и однозначен.
Заключение
Хотелось бы завершить парой слов. Я никому ничего не пытаюсь привить и навязать, эта статья не несёт революции, я лишь демонстрирую удачное совмещение инь и янь :), которое на практике разрушает традиционную критику табов. Я могу и ошибаться, что-то не учесть, хотя давно практикую этот метод и у меня пока нет к нему особых претензий.
Спасибо за внимание, всех благ, читатель! Надеюсь этот материал помог открыть что-то новое для тебя.
Примечания
[1] Стоит оговориться, что упомянутый недостаток не абсолютен как недостаток и имеет место из-за специфичного стиля написания кода программистом npm.
[2] Это по-моему следовало бы запретить, отсюда и эти самые святые войны растут, но также я считаю, что не следует утверждать ширину таба, т.к. в этом весь сок, как программисту удобно, такой ширины пусть таб себе и настраивает в редакторе, а другие могут настроить под себя, но в конечном итоге код будет одним и тем же.
[3] Иногда это полезно для избежания ошибок, например в моём js-коде можно встретить нечто подобное программисту npm, но только касательно секций var для переменных, потому как пропусти ты запятую справа, память может потечь как из ручья, а слева их видно сразу, для js-объектов это не актуально — сразу ошибка синтаксиса. Следует также брать в счёт моменты, когда посимвольное декорирование добавляет очевидности в код, например при разросшихся в длинну if-условий.
Комментариев нет:
Отправить комментарий