Спартанское программирование - PullRequest
9 голосов
/ 09 сентября 2008

Мне очень понравилось Пост Джеффа о Спартанском программировании . Я согласен, что такой код - радость для чтения. К сожалению, я не уверен, что работать с ним обязательно будет радостью.

В течение многих лет я читал и придерживался практики «одно выражение на строку». Я боролся за хорошую борьбу и держался на ногах, когда многие книги по программированию возражали против этого совета с примером кода как:

while (bytes = read(...))
{
   ...
}

while (GetMessage(...))
{
   ...
}

В последнее время я рекомендовал одно выражение в строке по более практическим причинам - отладка и поддержка производства. Получение файла журнала из производства, который запрашивает исключение NullPointer в «строке 65», которая гласит:

ObjectA a = getTheUser(session.getState().getAccount().getAccountNumber());

разочаровывает и его можно полностью избежать. Если не считать эксперта с кодом, который может выбрать «наиболее вероятный» объект, который был нулевым ... это настоящая практическая боль.

Одно выражение в строке также очень помогает при пошаговом выполнении кода. Я практикую это с предположением, что большинство современных компиляторов могут оптимизировать все лишние временные объекты, которые я только что создал ...

Я стараюсь быть аккуратным - но загромождать мой код явными объектами иногда кажется трудоемким. Как правило, он не облегчает просмотр кода, но он действительно пригодится при поиске вещей в процессе работы или при просмотре моего или чужого кода.

Какой стиль вы отстаиваете, и можете ли вы обосновать его в практическом смысле?

Ответы [ 8 ]

7 голосов
/ 09 сентября 2008

In Прагматичный программист Хант и Томас рассказывают об исследовании, которое они называют Законом Деметры, и оно фокусируется на соединении функций с модулями, отличными от собственных. Позволяя функции никогда не достигать 3-го уровня в своем соединении, вы значительно уменьшаете количество ошибок и повышаете удобство сопровождения кода.

Итак:

ObjectA a = getTheUser (session.getState (). GetAccount (). GetAccountNumber ());

Близко к уголовному преступлению, потому что мы 4 объекта в крысиной норе. Это значит что-то изменить в одном из этих объектов. Я должен знать, что вы назвали весь этот стек прямо здесь, в этом самом методе. Что за боль.

Лучше:

Account.getUser ();

Обратите внимание, что это идет вразрез с выразительными формами программирования, которые в настоящее время действительно популярны в программах для имитации. Компромисс в том, что у вас все равно есть тесно связанный интерфейс, а выразительный синтаксис просто упрощает его использование.

5 голосов
/ 09 сентября 2008

Я думаю, что идеальное решение - найти баланс между крайностями. Нет способа написать правило, которое подойдет для любых ситуаций; это приходит с опытом. Объявление каждой промежуточной переменной в отдельной строке усложнит чтение кода, что также усложнит обслуживание. Точно так же отладка намного сложнее, если вы укажете промежуточные значения.

«Сладкое пятно» где-то посередине.

4 голосов
/ 09 сентября 2008

Одно выражение на строку.

Нет причин запутывать ваш код. Дополнительное время, потраченное на ввод нескольких дополнительных терминов, экономит время отладки.

3 голосов
/ 09 сентября 2008

Я склонен ошибаться на стороне читабельности, а не отладки. Приведенных вами примеров определенно следует избегать, но я чувствую, что разумное использование нескольких выражений может сделать код более кратким и понятным.

2 голосов
/ 28 ноября 2010
ObjectA a = getTheUser(session.getState().getAccount().getAccountNumber());

Это плохой пример, возможно, потому, что вы только что написали что-то из головы. Вы присваиваете переменной с именем a типа ObjectA возвращаемое значение функции с именем getTheUser.
Итак, давайте предположим, что вы написали это вместо:

User u = getTheUser(session.getState().getAccount().getAccountNumber());

Я бы разбил это выражение так:

Account acc = session.getState().getAccount();
User user = getTheUser( acc.getAccountNumber() );

Я рассуждаю так: как бы я подумал о том, что я делаю с этим кодом?
Я, вероятно, подумал бы: «сначала мне нужно получить учетную запись из сеанса, а затем я получу пользователя, используя номер этой учетной записи».

Код должен читаться так, как вы думаете. Переменные должны относиться к основным вовлеченным объектам; не так сильно, как их свойства (поэтому я не буду хранить номер счета в переменной).

Второй фактор, который нужно иметь в виду: мне когда-нибудь понадобится снова обратиться к этой сущности в этом контексте?
Если, скажем, я вытаскиваю больше вещей из состояния сеанса, я бы ввел SessionState state = session.getState().

Все это кажется очевидным, но я боюсь, что мне трудно выразить словами, почему это имеет смысл, не будучи носителем английского языка и все такое.

2 голосов
/ 09 сентября 2008

Я обычно в лагере "чем короче, тем лучше". Ваш пример хорош:

ObjectA a = getTheUser(session.getState().getAccount().getAccountNumber());

Я бы съежился, если бы увидел более четырех строк вместо одной - не думаю, что это облегчит чтение или понимание. То, как вы представили это здесь, ясно, что вы копаете для одного объекта. Это не лучше:

obja State = session.getState();
objb Account = State.getAccount();
objc AccountNumber = Account.getAccountNumber();
ObjectA a = getTheUser(AccountNumber);

Это компромисс:

objb Account = session.getState().getAccount();
ObjectA a = getTheUser(Account.getAccountNumber());

но я все еще предпочитаю однострочное выражение. Вот анекдотическая причина: мне трудно перечитать и проверить ошибки на 4-х строчках прямо сейчас на наличие глупых опечаток; в отдельной строке такой проблемы нет, потому что символов просто меньше.

0 голосов
/ 09 сентября 2008

Хорошее объяснение. Я думаю, что это версия общего разделяй и властвуй менталитета.

0 голосов
/ 09 сентября 2008

Удобство обслуживания, а вместе с ним удобочитаемость - главное. К счастью, короче очень часто означает более читабельный.

Вот несколько советов, которые я люблю использовать для нарезки и нарезки кода:

  • Имена переменных : как бы вы описали эту переменную кому-то из вашей команды? Вы бы не сказали бы "целое число numberOfLinesSoFar". Вы бы сказали «numLines» или что-то подобное - понятное и краткое. Не притворяйтесь, будто сопровождающий вообще не знает код, но убедитесь, что вы сами можете выяснить, что это за переменная, даже если вы забыли свой собственный акт ее написания. Да, это очевидно, но это стоит больше усилий, чем я вижу, что многие кодировщики вкладывают в него, поэтому я перечислю это сначала.
  • Поток управления : Избегайте большого количества закрывающих предложений одновременно (серии} в C ++). Обычно, когда вы видите это, есть способ избежать этого. Обычный случай - это что-то вроде

if (things_are_ok) {
  // Do a lot of stuff.
  return true;
} else {
  ExpressDismay(error_str);
  return false;
}

можно заменить на

if (!things_are_ok) return ExpressDismay(error_str);
// Do a lot of stuff.
return true;

если мы сможем заставить ExpressDismay (или его оболочку) вернуть false.

Другой случай:

  • Итерации цикла : чем больше стандарт, тем лучше. Для более коротких циклов хорошо использовать односимвольные итераторы, когда переменная никогда не используется, кроме как в качестве индекса в одном объекте.

В данном случае я бы сказал, что против "правильного" способа использования контейнера STL:

for (vector<string>::iterator a_str = my_vec.begin(); a_str != my_vec.end(); ++a_str)

намного сложнее и требует перегруженных операторов указателя * a_str или a_str-> size () в цикле. Для контейнеров с быстрым произвольным доступом гораздо проще прочитать следующее:

for (int i = 0; i < my_vec.size(); ++i)

со ссылками на my_vec [i] в ​​теле цикла, что никого не смущает.

Наконец, я часто вижу, как кодеры гордятся количеством строк. Но это не номера строк, которые учитываются! Я не уверен в наилучшем способе реализации этого, но если вы будете иметь какое-либо влияние на вашу культуру кодирования, я постараюсь перенести вознаграждение на тех, у кого компактные классы :)

...