Во время проекта я заметил, что слишком много повторюсь с необязательными параметрами и настройками, поэтому я создал класс, который обрабатывает проверку типов и присваивает значение по умолчанию, что приводит к аккуратному и читабельному коду. Посмотрите пример и дайте мне знать, если это работает для вас.
var myCar = new Car('VW', {gearbox:'automatic', options:['radio', 'airbags 2x']});
var myOtherCar = new Car('Toyota');
function Car(brand, settings) {
this.brand = brand;
// readable and adjustable code
settings = DefaultValue.object(settings, {});
this.wheels = DefaultValue.number(settings.wheels, 4);
this.hasBreaks = DefaultValue.bool(settings.hasBreaks, true);
this.gearbox = DefaultValue.string(settings.gearbox, 'manual');
this.options = DefaultValue.array(settings.options, []);
// instead of doing this the hard way
settings = settings || {};
this.wheels = (!isNaN(settings.wheels)) ? settings.wheels : 4;
this.hasBreaks = (typeof settings.hasBreaks !== 'undefined') ? (settings.hasBreaks === true) : true;
this.gearbox = (typeof settings.gearbox === 'string') ? settings.gearbox : 'manual';
this.options = (typeof settings.options !== 'undefined' && Array.isArray(settings.options)) ? settings.options : [];
}
Используя этот класс:
(function(ns) {
var DefaultValue = {
object: function(input, defaultValue) {
if (typeof defaultValue !== 'object') throw new Error('invalid defaultValue type');
return (typeof input !== 'undefined') ? input : defaultValue;
},
bool: function(input, defaultValue) {
if (typeof defaultValue !== 'boolean') throw new Error('invalid defaultValue type');
return (typeof input !== 'undefined') ? (input === true) : defaultValue;
},
number: function(input, defaultValue) {
if (isNaN(defaultValue)) throw new Error('invalid defaultValue type');
return (typeof input !== 'undefined' && !isNaN(input)) ? parseFloat(input) : defaultValue;
},
// wrap the input in an array if it is not undefined and not an array, for your convenience
array: function(input, defaultValue) {
if (typeof defaultValue === 'undefined') throw new Error('invalid defaultValue type');
return (typeof input !== 'undefined') ? (Array.isArray(input) ? input : [input]) : defaultValue;
},
string: function(input, defaultValue) {
if (typeof defaultValue !== 'string') throw new Error('invalid defaultValue type');
return (typeof input === 'string') ? input : defaultValue;
},
};
ns.DefaultValue = DefaultValue;
}(this));
Я могу поймать зениту за это, но читабельность. Сильно последовательное, но независимое выполнение, которое может быть разбито на N вызовов функций (функций, которые больше нигде не используются), на самом деле не выигрывает от декомпозиции. Если вы не считаете достижение произвольного максимума длины функции преимуществом.
Я бы предпочел прокрутить по порядку N блоков кода размером с функцию, чем перемещаться по всему файлу, нажимая N функций.
Несколько идей, еще не упомянутых явно:
Код синтаксического анализа XML часто содержит множество операций обработки escape-символов в одной функции настройки.
Функции, с которыми я работаю (не пишу), становятся длинными, потому что они расширяются и расширяются, и никто не тратит время на повторное разложение функций. Они просто добавляют логику к функциям, не задумываясь об общей картине.
Я часто занимаюсь разработкой «вырезать и вставить» ...
Итак, для статьи, один аспект, на который следует обратить внимание, - это плохой план / цикл обслуживания и т. Д.
С другой стороны, точность
time ()
иsleep ()
лучше, чем их эквиваленты в Unix: время выражается в виде чисел с плавающей запятой,time ()
возвращает наиболее точное время доступно (с использованием Unixgettimeofday
где доступно), аsleep ()
будет принять время с ненулевой дробью (Unixselect
используется для реализации это, если доступно).
И более конкретно wrt sleep ()
:
Приостановить выполнение для данного числа секунд. Аргументом может быть число с плавающей запятой для обозначения более точное время сна. Настоящий время приостановки может быть меньше запрошен, потому что любой пойманный сигнал завершит
sleep ()
после выполнение перехвата этого сигнала рутина. Кроме того, время приостановки может быть длиннее , чем требует произвольная сумма из-за планирование другой деятельности в затем снова сохранение в стеке, затем снова прыжок. если вы используете параметры функции, у вас обычно есть еще несколько нажатий.Рассмотрим цикл:
for... func1
внутри цикла все эти нажатия и прыжки могут быть фактором.
Это было в значительной степени решено с помощью представления из Встроенные функции на C99 и неофициально до этого, но некоторый код, написанный ранее или создаваемый с учетом совместимости, мог быть длинным по этой причине.
Также Inline имеет его потоки, некоторые из них описаны в ссылке на встроенные функции .
Изменить:
В качестве примера того, как вызов функции может замедлить выполнение программы:
4 static void 5 do_printf() 6 { 7 printf("hi"); 8 } 9 int 10 main() 11 { 12 int i=0; 13 for(i=0;i<1000;++i) 14 do_printf(); 15 }
Это приводит к (GCC 4.2 .4):
. . jmp .L4 .L5: call do_printf addl $1, -8(%ebp) .L4: cmpl $999, -8(%ebp) jle .L5 . . do_printf: pushl %ebp movl %esp, %ebp subl $8, %esp movl $.LC0, (%esp) call printf leave ret
против:
int main() { int i=0; for(i=0;i<1000;++i) printf("hi"); }
или против:
4 static inline void __attribute__((always_inline)) //This is GCC specific! 5 do_printf() 6 { 7 printf("hi"); 8 }
Оба производят (GCC 4.2.4):
jmp .L2 .L3: movl $.LC0, (%esp) call printf addl $1, -8(%ebp) .L2: cmpl $999, -8(%ebp) jle .L3
Что быстрее.
Чаще всего я вижу / пишу длинные операторы переключения или операторы полусопереключения if / else для типов, которые нельзя использовать в операторах переключения этого языка (уже упоминалось несколько раз). Сгенерированный код - интересный случай, но здесь я сосредоточен на коде, написанном человеком. Глядя на мой текущий проект, единственная действительно длинная функция, не включенная выше (296 LOC / 650 LOT), - это некоторый Cowboy Code, который я использую в качестве ранней оценки вывода генератора кода, который я планирую использовать в будущем. Я определенно проведу его рефакторинг, что удалит его из этого списка.
Много лет назад я работал над некоторым программным обеспечением для научных вычислений, которое имело долгую функцию. В методе использовалось большое количество локальных переменных, а рефакторинг метода оставался неизменным, что приводило к измеримой разнице при профилировании. Даже улучшение на 1% в этом разделе кода сэкономило часы вычислительного времени, так что функция оставалась надолго. С тех пор я многому научился, поэтому не могу сказать, как бы справился с ситуацией сегодня.
Очень длинные функции, с которыми я сталкиваюсь, написаны не на C, поэтому вы Придется решить, относится ли это к вашему исследованию или нет. Я имею в виду некоторые функции PowerBuilder длиной в несколько сотен строк по следующим причинам:
Это еще одно место, где очевидные плохие методы программирования встречаются с реальностью. В то время как любой первокурсник CS мог бы сказать, что эти звери плохие, никто не станет тратить деньги на то, чтобы они выглядели красивее (учитывая, что, по крайней мере, на данный момент, они все еще работают).
Один момент, который, как мне кажется, имеет значение, заключается в том, что разные языки и инструменты имеют разную лексическую область видимости, связанную с функциями.
Например, Java позволяет подавлять предупреждения с помощью аннотации. Может быть желательно ограничить объем аннотации, и поэтому для этой цели вы должны использовать короткую функцию. На другом языке разбиение этого раздела на его собственную функцию может быть совершенно произвольным.
Спорный вопрос: в JavaScript я обычно создаю функции только с целью повторного использования кода. Если фрагмент выполняется только в одном месте, я считаю обременительным перемещаться по файлу (файлам) после спагетти ссылок на функции. Я думаю, что закрытие облегчает и, следовательно, усиливает более длительные [родительские] функции. Поскольку JS - это интерпретируемый язык, а фактический код пересылается по сети, он ' Хорошо, если длина кода будет небольшой - создание соответствующих объявлений и ссылок не помогает (это можно рассматривать как преждевременную оптимизацию). Функция должна быть довольно длинной в JS, прежде чем я решу нарезать ее с явной целью «держать функции короткими».
Опять же в JS, иногда весь «класс» технически является функцией со многими вложенными подфункциями но есть инструменты, которые помогут с этим справиться.
С другой стороны, в JS переменные имеют область видимости для длины функции, и это фактор, который может ограничивать длину данной функции.
Сгенерированный код может генерировать очень-очень длинные функции.
Единственные, которые я недавно кодировал, - это то, где не удается сделать их меньше или сделать код менее читаемым. Представление о том, что функция, длина которой превышает определенную длину, по своей сути является плохой, является просто слепой догмой. Как любая слепо применяемая догма, она избавляет последователя от необходимости думать о том, что применимо в каждом конкретном случае ...
Недавние примеры ...
Анализ и проверка конфигурационного файла с простой структурой имя = значение в массив, конвертирующий каждое значение, как я его нахожу, это один массивный оператор переключения, один случай для каждой опции конфигурации. Почему? Я мог бы разбить на множество вызовов тривиальных функций 5/6 строк. Это добавило бы к моему классу около 20 частных членов. Ни один из них больше нигде не используется. Разделение его на более мелкие части просто не добавляло достаточно ценности, чтобы того стоить, так было с момента создания прототипа. Если мне нужен другой вариант, добавьте еще один случай.
Другой случай - это код взаимодействия клиента и сервера в одном приложении и его клиент. Множество вызовов чтения / записи, любой из которых может завершиться ошибкой, и в этом случае я отключаюсь и возвращаю false. Таким образом, эта функция в основном линейна и имеет точки возврата (в случае неудачи - возврат) почти после каждого вызова. Опять же, ничего не выиграешь, сделав его меньше, и нет возможности сделать его еще меньше.
Я также должен добавить, что большинство моих функций - это пара "экранов", и я стараюсь в более сложных областях, чтобы сохранить их на одном уровне. "screenful" просто потому, что я могу просмотреть всю функцию сразу. Это нормально для функций, которые в основном являются линейными по своей природе и не имеют большого количества сложных циклов или условий, поэтому поток прост. с момента создания прототипа. Если мне нужен другой вариант, добавьте еще один случай.
Другой случай - это код взаимодействия клиента и сервера в одном приложении и его клиент. Множество вызовов чтения / записи, любой из которых может завершиться ошибкой, и в этом случае я возвращаю ложь. Таким образом, эта функция в основном линейна и имеет точки возврата (в случае неудачи - возврат) почти после каждого вызова. Опять же, ничего не выиграешь, сделав его меньше, и нет возможности сделать его еще меньше.
Я также должен добавить, что большинство моих функций - это пара "экранов", и я стараюсь в более сложных областях, чтобы сохранить их на одном уровне. "screenful" просто потому, что я могу сразу просмотреть всю функцию. Это нормально для функций, которые в основном являются линейными по своей природе и не имеют большого количества сложных циклов или условий, поэтому поток прост. с момента создания прототипа. Если мне нужен другой вариант, добавьте еще один случай.
Другой случай - это код взаимодействия клиента и сервера в одном приложении и его клиент. Множество вызовов чтения / записи, любой из которых может завершиться ошибкой, и в этом случае я отключаюсь и возвращаю false. Таким образом, эта функция в основном линейна и имеет точки возврата (в случае неудачи - возврат) почти после каждого вызова. Опять же, ничего не выиграешь, сделав его меньше, и нет возможности сделать его еще меньше.
Я также должен добавить, что большинство моих функций - это пара "экранов", и я стараюсь в более сложных областях, чтобы сохранить их на одном уровне. "screenful" просто потому, что я могу просмотреть всю функцию сразу. Это нормально для функций, которые в основном являются линейными по своей природе и не имеют большого количества сложных циклов или условий, поэтому поток прост. В заключение я предпочитаю применять соображения рентабельности при принятии решения, какой код рефакторировать, и соответственно расставлять приоритеты. Помогает избежать постоянно незавершенного проекта.
Прочтите главу McConnell's Code Complete о подпрограммах, в ней есть рекомендации и указатели того, когда следует разбивать вещи на функции. Если у вас есть алгоритм, в котором эти правила не применяются, это может быть хорошей причиной для использования длинной функции.
Функции со временем могут стать длиннее, особенно если они модифицируются многими группами разработчиков.
Показательный пример: я недавно (~ год или 2 назад) реорганизовал некоторый устаревший код обработки изображений с 2001 года или около того, это содержало несколько функций на несколько тысяч строк. Не несколько файлов с несколькими тысячами строк - несколько функций с несколькими тысячами строк.
С годами к ним было добавлено так много функциональных возможностей, не прилагая усилий для их правильного рефакторинга.
Все, что сгенерировано из других источников, т. Е. Конечный автомат из генератора синтаксического анализатора или аналогичного. Если он не предназначен для употребления в пищу, проблемы эстетики или ремонтопригодности не имеют значения.
Иногда я пишу плоский файл (для использования третьими сторонами), который включает в себя заголовки, трейлеры и подробные записи, которые все связаны. Проще иметь длинную функцию для вычисления итогов, чем разработать некую схему для передачи значений туда и обратно через множество небольших функций.