Мой стиль кодирования для вызовов вложенных функций следующий:
var result_h1 = H1(b1);
var result_h2 = H2(b2);
var result_g1 = G1(result_h1, result_h2);
var result_g2 = G2(c1);
var a = F(result_g1, result_g2);
Недавно я перешел в отдел, где очень популярен следующий стиль кодирования:
var a = F(G1(H1(b1), H2(b2)), G2(c1));
Результатом моего способа кодирования является то, что в случае сбойной функции Visual Studio может открыть соответствующий дамп и указать строку, в которой возникает проблема (меня особенно беспокоит нарушение доступа).
Я боюсь, что в случае сбоя из-за той же проблемы, запрограммированной первым способом, я не смогу узнать, какая функция вызвала сбой.
С другой стороны, чем больше обработки вы помещаете в строку, тем больше логики вы получаете на одной странице, что повышает удобочитаемость.
Мой страх верен или я что-то упускаю, и вообще, что предпочтительнее в коммерческой среде? Читаемость или ремонтопригодность?
Я не знаю, уместно ли это, но мы работаем на C ++ (STL) / C #.
источник
HX
иGX
вызовов могут изменяться в однострочнике, а порядок вычисления аргументов функции не указан. Если вы по какой-то причине зависите от порядка побочных эффектов (сознательно или неосознанно) в вызовах, этот «рефакторинг стиля» может в конечном итоге повлиять не только на читаемость / обслуживание.result_g1
которое вы на самом деле используете, или это значение действительно представляет что-то с разумным именем; напримерpercentageIncreasePerSecond
. Это было бы моим тестом, чтобы выбрать между двумяОтветы:
Если вы были вынуждены расширить один лайнер, как
Я не буду винить тебя. Это не только трудно читать, но и трудно отлаживать.
Почему?
Если вы расширите его с промежуточными результатами, вы получите
и это все еще трудно читать. Почему? Это решает две проблемы и вводит четвертую:
Это плотноНекоторые отладчики только выдвигают на первый план все сразуЕсли вы расширите его именами, которые добавляют новое, хорошее, семантическое значение, даже лучше! Хорошее имя помогает мне понять.
Теперь, по крайней мере, это рассказывает историю. Это решает проблемы и явно лучше, чем все остальное, что предлагается здесь, но требует от вас придумать имена.
Если вы делаете это с бессмысленными именами, такими как,
result_this
иresult_that
потому что вы просто не можете думать о хороших именах, то я бы действительно предпочел, чтобы вы избавили нас от бесполезного загромождения имен и расширили его, используя старые добрые пробелы:Он так же удобен для чтения, если не больше, чем тот, который имеет бессмысленные имена результатов (не то, чтобы эти имена функций были такими хорошими).
Это плотноНекоторые отладчики только выдвигают на первый план все сразуЭто загромождено неописательными именамиКогда вы не можете думать о хороших именах, это так же хорошо, как и получается.
По некоторым причинам отладчики любят новые строки, поэтому вы должны обнаружить, что отладка это не сложно:
Если этого недостаточно, представьте, что вам
G2()
позвонили более чем в одном месте, и тогда это произошло:Я думаю, это хорошо, что, поскольку каждый
G2()
вызов будет на отдельной линии, этот стиль приведет вас непосредственно к вызову-нарушителю в main.Поэтому, пожалуйста, не используйте проблемы 1 и 2 в качестве предлога, чтобы увязать нас с проблемой 4. Используйте добрые имена, когда вы можете думать о них. Избегайте бессмысленных имен, когда вы не можете.
Гонки Легкости в комментарии Orbit правильно указывают на то, что эти функции являются искусственными и имеют мертвые плохие имена. Итак, вот пример применения этого стиля к некоторому дикому коду:
Я ненавижу смотреть на этот поток шума, даже когда перенос слов не нужен. Вот как это выглядит в этом стиле:
Как вы можете видеть, я обнаружил, что этот стиль хорошо работает с функциональным кодом, который перемещается в объектно-ориентированное пространство. Если вы можете придумать добрые имена, чтобы сделать это в промежуточном стиле, тогда вам больше под силу. До этого я использую это. Но в любом случае, пожалуйста, найдите способ избежать бессмысленных имен результатов. Они делают мои глаза болят.
источник
Я совершенно не согласен с этим. Просто глядя на ваши два примера кода, это неверно:
слышно читать. «Читаемость» не означает плотность информации; это означает «легко читать, понимать и поддерживать».
Иногда код прост и имеет смысл использовать одну строку. В других случаях это только затрудняет чтение, без какой-либо очевидной выгоды, за исключением того, что вы помещаете больше в одну строку.
Тем не менее, я бы также призвал вас заявить, что «легко диагностировать сбои» означает, что код легко поддерживать. Код, который не падает, гораздо проще поддерживать. «Простота обслуживания» достигается, прежде всего, тем, что код легко читается и понимается, а также подкреплен хорошим набором автоматических тестов.
Поэтому, если вы превращаете одно выражение в многострочное с множеством переменных только потому, что ваш код часто дает сбой и вам нужна лучшая отладочная информация, тогда прекратите делать это и сделайте код более устойчивым. Вы должны предпочесть написание кода, который не требует отладки, а не кода, который легко отлаживать.
источник
F(G1(H1(b1), H2(b2)), G2(c1))
это трудно читать, это не имеет ничего общего с тем, чтобы быть слишком плотным. (Не уверен, что вы хотели сказать это, но это можно интерпретировать таким образом.) Вложение трех или четырех функций в одну строку может быть легко читаемым, особенно если некоторые функции являются простыми инфиксными операторами. Проблема заключается в неописательных именах, но эта проблема еще хуже в многострочной версии, где вводится еще больше неописательных имен . Добавление только шаблона практически никогда не способствует удобочитаемости.G1
принимает ли 3 параметра или только 2 иG2
является ли другим параметромF
. Я должен щуриться и считать скобки.F (G1 (H1 b1) (H2 b2)) (G2 c1)
)result_h1
не может быть повторно использована, если она не существует, и расположение между 4 переменными очевидно.Ваш первый пример, форма одиночного присваивания, не читается, потому что выбранные имена совершенно бессмысленны. Это может быть артефактом попытки не разглашать внутреннюю информацию с вашей стороны, истинный код может подойти в этом отношении, мы не можем сказать. Как бы то ни было, это очень скучно из-за чрезвычайно низкой плотности информации, которая, как правило, не поддается легкому пониманию.
Ваш второй пример сжат до абсурда. Если бы у функций были полезные имена, это могло бы быть хорошо и хорошо читаемо, потому что их не слишком много , но как-то это сбивает с толку в другом направлении.
После введения значимых имен вы можете посмотреть, выглядит ли одна из форм естественной или есть золотая середина, по которой можно стрелять.
Теперь, когда у вас есть читаемый код, большинство ошибок станут очевидными, а другим, по крайней мере, будет труднее скрываться от вас.
источник
Как всегда, когда дело доходит до читабельности, неудачи в крайности . Вы можете получить любой хороший совет по программированию, превратить его в религиозное правило и использовать его для создания совершенно нечитаемого кода. (Если вы мне не верите, посмотрите на этих двух победителей IOCCC, борсаний и горенов, и посмотрите, как по-разному они используют функции, чтобы сделать код совершенно нечитаемым. Подсказка: Борсани использует ровно одну функцию, намного, гораздо больше ...)
В вашем случае двумя крайностями являются: 1) использование только выражений с одним выражением и 2) объединение всего в большие, краткие и сложные выражения. Любой из этих подходов делает ваш код нечитаемым.
Ваша задача как программиста - найти баланс . Для каждого написанного вами утверждения ваша задача - ответить на вопрос: «Легко ли понять это утверждение и служит ли оно для того, чтобы сделать мою функцию читабельной?»
Дело в том, что нет единой измеримой сложности оператора, которая могла бы решить, что хорошо для включения в одно утверждение. Возьмем для примера строку:
Это довольно сложное утверждение, но любой программист, достойный их соли, должен быть в состоянии немедленно понять, что это делает. Это довольно известный шаблон. Как таковой, он гораздо более читабелен, чем эквивалент
который разбивает хорошо известный шаблон на кажущееся бессмысленным количество простых шагов. Однако утверждение из вашего вопроса
мне кажется слишком сложным, хотя это на одну операцию меньше, чем вычисление расстояния . Конечно, это является прямым следствием меня ничего не зная о том
F()
,G1()
,G2()
,H1()
, илиH2()
. Я мог бы решить по-другому, если бы я знал о них больше. Но в этом-то и проблема: рекомендуемая сложность утверждения сильно зависит от контекста и задействованных операций. И вы, как программист, должны взглянуть на этот контекст и решить, что включить в одно утверждение. Если вы заботитесь о читабельности, вы не можете переложить эту ответственность на какое-то статическое правило.источник
@ Доминик, я думаю, что в анализе вашего вопроса вы делаете ошибку, что «удобочитаемость» и «ремонтопригодность» - это две разные вещи.
Возможно ли иметь код, который поддерживается, но не читается? И наоборот, если код чрезвычайно читабелен, почему он становится не поддерживаемым из-за читабельности? Я никогда не слышал о программистах, которые сравнивали бы эти факторы друг с другом, выбирая один или другой!
С точки зрения принятия решения о том, использовать ли промежуточные переменные для вызовов вложенных функций, в случае 3 заданных переменных, вызовов 5 отдельных функций и некоторых вызовов с вложенной глубиной 3, я бы склонялся к использованию по крайней мере некоторых промежуточных переменных для их разбивки, как вы сделали.
Но я, конечно, не захожу так далеко, чтобы сказать, что вызовы функций никогда не должны быть вложенными вообще. Это вопрос суждения в данных обстоятельствах.
Я бы сказал, что на решение влияют следующие моменты:
Если вызываемые функции представляют стандартные математические операции, они более способны к вложению, чем функции, которые представляют некоторую непонятную логику области, результаты которой непредсказуемы и не могут быть обязательно оценены читателем мысленно.
Функция с одним параметром более способна участвовать в гнезде (как внутренняя или внешняя функция), чем функция с несколькими параметрами. Смешивание функций разных арностей на разных уровнях вложенности склонно оставлять код похожим на ухо свиньи.
Гнездо функций, которое программисты привыкли видеть выраженным определенным образом - возможно, потому, что оно представляет стандартную математическую технику или уравнение, которое имеет стандартную реализацию - может быть более трудным для чтения и проверки, если оно разбито на промежуточные переменные.
Небольшое гнездо вызовов функций, которое выполняет простую функциональность и уже понятно для чтения, а затем разбито и разбито, способно быть более трудным для чтения, чем то, которое вообще не разбивалось.
источник
Оба субоптимальны. Учитывайте комментарии.
Или конкретные функции, а не общие:
При принятии решения о том, какие результаты следует изложить, следует учитывать стоимость (копия против ссылки, l-значение против r-значения), читаемость и риск в отдельности для каждого оператора.
Например, нет никакой дополнительной выгоды от перемещения простых преобразований единиц / типов в их собственные строки, потому что они легко читаются и крайне маловероятны:
Что касается вашего беспокойства по поводу анализа аварийных дампов, проверка входных данных, как правило, гораздо важнее - скорее всего, настоящий сбой произойдет внутри этих функций, а не в строке, вызывающей их, и даже если нет, вам обычно не нужно точно указывать, где все взорвалось. Гораздо важнее знать, где вещи начали разваливаться, чем знать, где они окончательно взорвались, что и ловит входная проверка.
источник
Читаемость является основной частью ремонтопригодности. Сомневаться во мне? Выберите большой проект на языке, который вы не знаете (желательно, как на языке программирования, так и на языке программистов), и посмотрите, как вы будете проводить его рефакторинг ...
Я бы поставил удобочитаемость где-то между 80 и 90 ремонтопригодностью. Остальные 10-20 процентов - насколько это поддается рефакторингу.
Тем не менее, вы эффективно передаете 2 переменные вашей конечной функции (F). Эти 2 переменные создаются с использованием 3 других переменных. Было бы лучше передать b1, b2 и c1 в F, если F уже существует, тогда создайте D, который выполняет композицию для F и возвращает результат. На этом этапе нужно просто дать D хорошее имя, и не имеет значения, какой стиль вы используете.
Что касается не связанных, вы говорите, что больше логики на странице способствует удобочитаемости. Это неверно, метрика - это не страница, это метод, и чем меньше логика, содержащаяся в методе, тем она более читабельна.
Читаемый означает, что программист может держать логику (вход, выход и алгоритм) в своей голове. Чем больше, тем МЕНЬШЕ программист может понять. Читайте о цикломатической сложности.
источник
Независимо от того, находитесь ли вы в C # или C ++, пока вы находитесь в отладочной сборке, возможное решение заключается в переносе функций
Вы можете написать oneline выражение, и, тем не менее, указать, где проблема, просто посмотрев трассировку стека.
Конечно, если вы вызываете одну и ту же функцию несколько раз в одной и той же строке, вы не можете знать, какую функцию, однако вы все равно можете ее идентифицировать:
Это не серебряная пуля, но не так уж плохо на полпути.
Не говоря уже о том, что группа функций для переноса может быть даже более полезной для читабельности кода:
источник
На мой взгляд, самодокументированный код лучше как для удобства обслуживания, так и для удобства чтения, независимо от языка.
Вышеупомянутое утверждение плотное, но «самодокументированное»:
Когда разбивается на этапы (проще для тестирования, конечно) теряет весь контекст, как указано выше:
И очевидно, что использование имен переменных и функций, в которых четко указана их цель, неоценимо.
Даже блоки «если» могут быть хорошими или плохими в самодокументировании. Это плохо, потому что вы не можете легко заставить первые 2 условия проверить третье ... все они не связаны:
Этот имеет больше «коллективного» смысла и легче создавать условия тестирования:
И это утверждение - просто случайная строка символов, рассматриваемая с точки зрения самодокументирования:
Глядя на вышеприведенное утверждение, ремонтопригодность по-прежнему является серьезной проблемой, если функции H1 и H2 обе изменяют одни и те же «переменные состояния системы» вместо объединения в одну функцию «H», потому что кто-то в конечном итоге изменит H1, даже не думая, что существует H2 функция, чтобы смотреть и может сломать H2.
Я считаю, что хороший дизайн кода очень сложен, потому что нет жестких правил, которые можно систематически обнаруживать и применять.
источник