2010-04-12 5 views
8

Manypeople поспорили о функции размер. Говорят, что в целом функции должны быть довольно короткими. Мнения варьируются от примерно 15 строк до «около одного экрана», который сегодня, вероятно, составляет около 40-80 строк.
Кроме того, функции всегда должны выполнять только одну задачу.Неужели плохая практика имеет длительный метод инициализации?

Однако существует один вид функции, которая часто терпит неудачу в обоих критериях в моем коде: функции инициализации.

Например, в аудиоприложении звуковое оборудование/API необходимо настроить, аудиоданные должны быть преобразованы в подходящий формат и состояние объекта должно быть правильно инициализировано. Это, очевидно, три разные задачи, и в зависимости от API это может легко охватывать более 50 строк.

Вещь с init-функциями заключается в том, что их обычно вызывают только один раз, поэтому нет необходимости повторно использовать какие-либо компоненты. Не могли бы вы разбить их на несколько более мелких функций, если бы вы считали, что большие функции инициализации в порядке?

ответ

12

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

void _init_hardware() { } 
void _convert_format() { } 
void _setup_state() { } 

void initialize_audio() { 
    _init_hardware(); 
    _convert_format(); 
    _setup_state(); 
} 

Дать емкие функции как много о выделении вины и изменений как сохранение читаемости. Если вы знаете, что сбой находится в _convert_format(), вы можете отслеживать 40 строк, ответственных за ошибку, довольно быстро. То же самое применяется, если вы совершаете изменения, которые касаются только одной функции.

Последний вопрос, я использую assert() довольно часто, поэтому я могу «терпеть неудачу часто и терпеть неудачу рано», а начало функции - лучшее место для пары утверждений о проверке работоспособности.Хранение функции позволяет вам более тщательно тестировать функцию на основе более узкого набора обязанностей. Очень сложно выполнить единичную проверку функции 400 строк, которая делает 10 разных вещей.

+1

+1 для 'утверждают()' в одиночку. – ndim

+0

+1: «нет необходимости повторно использовать какие-либо компоненты». Повторное использование не является проблемой. Написание чего-то, что можно понять и поддерживать другими людьми, гораздо важнее. –

+0

Я помню часть рекомендаций о том, чтобы оставить идентификаторы, начинающиеся с подчеркивания, во внутреннем использовании компилятора C и избегая их в программах. Кроме того, вы должны отметить эти три функции init-off init как 'static'. На этот раз они не будут использоваться за пределами текущего исходного файла. И в качестве дополнительного преимущества интеллектуальный компилятор увидит, что их вызывают только один раз, и просто вставляйте код (на всякий случай, если вы беспокоитесь об этом накладные расходы). – ndim

5

Если разбивка на более мелкие части делает код более структурированным и/или более читаемым - сделайте это независимо от того, что делает функция. Это не о количестве строк о качестве кода.

2

В такой ситуации, я думаю, это сводится к личным предпочтениям. Я предпочитаю, чтобы функции выполняли только одно, поэтому я бы разделил инициализацию на отдельные функции, даже если они вызываются только один раз. Однако, если кто-то хотел сделать все это в одной функции, я бы не стал слишком беспокоиться об этом (пока код был ясен). Есть более важные вещи, чтобы спорить (например, как фигурные фигурные скобки принадлежат отдельной отдельной линии).

1

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

Альтернативой этому является использование инфраструктуры впрыска зависимостей (например, Spring, Castle Windsor, Guice и т. Д.). У этого есть определенные плюсы и минусы ... в то время как ваш путь через один большой метод может быть довольно болезненным, вы, по крайней мере, имеете хорошее представление о том, где все инициализировано, и нет необходимости беспокоиться о том, что может произойти «магия» , И снова инициализация не может быть изменена после развертывания (например, с помощью файла XML для Spring).

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

3

Я все равно попытаюсь разбить функции на логические единицы. Они должны быть такими же длинными или короткими, как это имеет смысл. Например:

SetupAudioHardware(); 
ConvertAudioData(); 
SetupState(); 

Назначение им понятных имен делает все более понятным и понятным. Кроме того, их разломы облегчают будущие изменения и/или другие программы для их повторного использования.

1

Во-первых, вместо функции инициализации следует использовать фабрику. То есть, вместо initialize_audio(), у вас есть new AudioObjectFactory (вы можете придумать лучшее имя здесь). Это поддерживает разделение проблем.

Однако будьте осторожны и не слишком абстрактны. Очевидно, что у вас есть две проблемы: 1) инициализация аудио и 2) использование этого аудио. До тех пор, пока, например, вы не отрисуете аудиоустройство, которое должно быть инициализировано, или способ, которым данное устройство может быть настроено во время инициализации, ваш заводский метод (audioObjectFactory.Create() или что-то еще) должен действительно храниться только одним большим методом. Ранняя абстракция служит только для запутывания дизайна.

Отметьте, что audioObjectFactory.Create() не является чем-то, что может быть подвергнуто тестированию. Тестирование - это интеграционный тест, и пока его части не будут абстрагированы, он останется интеграционным тестом. Позже вы обнаружите, что у вас несколько разных фабрик для разных конфигураций; в этот момент было бы полезно отвлечь аппаратные вызовы в интерфейсе, чтобы вы могли создавать модульные тесты, чтобы гарантировать, что различные заводы настроят оборудование надлежащим образом.

+0

Собственно, это в значительной степени то, что я делаю. Сам класс должен быть объектом аудиоплеера, который абстрагирует обработку беспорядка, который является CoreAudio. Действительно, подготовка аудиоданных и инициализация аудиооборудования очень тесно связаны, поскольку разные аудиоданные требуют другой конфигурации и наоборот. – bastibe

1

Я думаю, что это неправильный подход, чтобы попытаться подсчитать количество строк и определить функции, основанные на этом. Для чего-то вроде кода инициализации у меня часто есть отдельная функция для него, но в основном для того, чтобы функции Load или Init или New не были загроможденными и запутанными. Если вы можете разделить его на несколько задач, как предложили другие, то вы можете назвать его чем-то полезным и помочь организовать. Даже если вы вызываете его только один раз, это не плохая привычка, и часто вы обнаружите, что есть другие моменты, когда вы можете захотеть перезапустить вещи и снова использовать эту функцию.

1

Просто подумал, что я брошу это там, так как он еще не упоминался - Facade Pattern иногда упоминается как интерфейс к сложной подсистеме. Я не много сделал с ним сам, но метафоры, как правило, это что-то вроде включения компьютера (требуется несколько шагов) или включения системы домашнего кинотеатра (включить телевизор, включить приемник, выключить свет и т. Д. .)

В зависимости от структуры кода, возможно, стоит подумать о том, чтобы отвлечь ваши большие функции инициализации. Я до сих пор согласен с точкой зрения Magar, хотя функции разбиения на _init_X(), _init_Y() и т. Д. - хороший способ. Даже если вы не собираетесь повторно использовать комментарии в этом коде, в следующем проекте, когда вы говорите себе: «Как я инициализировал этот X-компонент?», Будет намного проще вернуться и выбрать его меньшей функции _init_X(), чем было бы выбрать ее из большей функции, особенно если X-инициализация разбросана по всей ее части.

1

Длина функции, как вы отметили, очень субъективная тема. Однако стандартная передовая практика заключается в том, чтобы изолировать код, который часто повторяется и/или может функционировать как его собственная сущность. Например, если ваша функция инициализации загружает файлы библиотеки или объекты, которые будут использоваться конкретной библиотекой, этот блок кода должен быть модульным.

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

Надежда, что помогает,
Карлос Нуньес

Смежные вопросы