Что такое рефакторинг, а что только модифицирует код?

81

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

Я считаю рефакторинг такими вещами, как извлечение методов и переименование классов. Они также предложили такие вещи, как изменение структур данных (например, Java LinkedListна an ArrayList), изменение алгоритмов (с использованием сортировки слиянием вместо пузырьковой) и даже переписывание больших фрагментов кода в качестве рефакторинга.

Я был совершенно уверен, что они ошибались, но я не мог объяснить причину, потому что то, что они предлагали, действительно изменило программу (и, предположительно, улучшило ее), не изменив ее поведения. Я прав и, что более важно, почему?

Дэвид Джонстон
источник

Ответы:

77

«Рефакторинг: улучшение дизайна существующего кода» Мартина Фаулера - это, пожалуй, ТОЧНАЯ ссылка:

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

Рефакторинг идет рука об руку с модульным тестированием. Напишите тесты до рефакторинга, и тогда у вас будет уровень уверенности в рефакторинге (пропорциональный охвату тестов).

Хорошая ссылка: Информация о рефакторинге

Митч Уит
источник
16
Цитата Фаулера, конечно, актуальна, и да, она идет рука об руку с модульными тестами ... Но действительно ли это отвечает на заданные вопросы: упомянутые примеры являются рефакторингом или просто изменением кода? Кто прав, ОП или его коллеги и почему?
Jonik
36

Фаулер проводит четкую грань между изменениями в коде, которые влияют, и теми, которые не влияют на его поведение. Он называет те, которые этого не делают, «рефакторингом». Это является важным отличием, потому что если мы разделим нашу работу в рефакторинге и не рефакторинг кода модификации деятельности (Фаулер называет это «носить разные шляпы»), мы можем применить различные, нацеленные соответствующие методы.

Если мы проводим рефакторинг или модификацию кода с сохранением поведения:

  • все наши модульные тесты должны пройти до и после модификации
  • нам не нужно изменять какие-либо тесты или писать новые
  • мы ожидаем более чистого кода, когда мы закончим
  • мы не ожидаем нового поведения

Если мы делаем модификацию кода, изменяющую поведение:

  • мы ожидаем нового поведения
  • мы должны написать новые тесты
  • мы можем получить более грязный код, когда мы закончим (и затем мы должны его реорганизовать)

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

Карл Манастер
источник
3
+1, точно. Особенно обоснование, которое вы даете; запутанные ожидания. Когда я писал свой ответ, я имел это в виду, даже если я не смог записать его так аккуратно :)
Jonik
18

Чтобы высказать свое мнение:

Небольшие инкрементальные изменения, которые оставляют код в лучшем состоянии, чем он был найден

Определенно да: «косметические» изменения, которые напрямую не связаны с функциями (т. Е. Не оплачиваются как запрос на изменение).

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

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


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

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

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


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

Точно так же изменение имени переменной или извлечение функции сами по себе не являются ощутимыми улучшениями. Но все вместе они борются с медленной эрозией.

Как стена из гальки, где каждый день падает на землю. И каждый день один прохожий поднимает его и кладет обратно.

Peterchen
источник
12

Принимая во внимание определение Мартина Фаулера,

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

... Думаю, вы однозначно правы.

Они также предложили такие вещи, как изменение структур данных (например, Java LinkedList на ArrayList), изменение алгоритмов (с использованием сортировки слиянием вместо пузырьковой сортировки) и даже переписывание больших фрагментов кода в качестве рефакторинга.

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

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

Йоник
источник
3
Я не уверен, что ускорение кода квалифицируется как изменение внешнего поведения ...
GalacticCowboy,
2
Да, я понимаю вашу точку зрения, полагаю, это зависит от того, под каким углом вы на это смотрите. :) В любом случае, IMO, при программировании нужно обращать внимание на то, какую «шляпу» вы носите в каждый момент, то есть чего именно вы пытаетесь достичь. Другими словами, вы должны сознательно разделять, когда вы добавляете / исправляете функцию, при рефакторинге и при оптимизации (повышении производительности). IIRC, Фаулер также говорит об этом в своей исчерпывающей книге по рефакторингу.
Jonik
1
Что касается части внешнего изменения, мы могли бы перефразировать, чтобы сказать: [...] без изменения его внешнего поведения, если поведение важно. Если производительность важна в любом случае (быстрее или медленнее), не вносите изменения, которые могут повлиять на нее, в рамках фазы «рефакторинга».
Loki
11

Если интерфейс к фрагменту кода меняется, я считаю, что это больше, чем рефакторинг.

Типичный случай рефакторинга:

  • «О, все мои модульные тесты выполняются, но я думаю, что мой код можно было бы сделать чище»
  • Измените код, чтобы он был более читабельным / ясным / эффективным
  • Повторно запустите модульные тесты (без изменения тестов) и проверьте, что они все еще работают

Это означает, что термин «рефакторинг» относится к интерфейсу, который вы обсуждаете. т.е. вы могли бы реорганизовать код одного интерфейса, в то же время более широко изменяя код другого на более низком уровне (может быть, это различие вызывает путаницу между вами и вашими коллегами?)

ДэнЗингерман
источник
7

Думаю, вы правы, но спорить о значении слова не особо интересно или продуктивно.

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

http://en.wikipedia.org/wiki/Code_refactoring

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

Я согласен с тем, что рефакторинг кода действительно включает нарушение существующего кода. Просто убедитесь, что у вас есть модульные тесты, чтобы не вводить никаких ошибок, а остальной код компилируется. Использование инструментов рефакторинга, таких как Resharper для C #, упрощает эту задачу!

  • Делаем код более понятным
  • Очистить код и сделать его более аккуратным
  • Удаление кода! Избыточный, неиспользуемый код и комментарии следует удалить.
  • Повышение производительности
  • Делаем что-нибудь более общее. Начните с самого простого, а затем реорганизуйте его, чтобы упростить тестирование / изоляцию или обобщение, чтобы он мог работать по-разному через полиморфизм
  • Сохранение кода DRY - не повторяйтесь, поэтому сеанс рефакторинга может включать в себя повторный код и его рефакторинг в один компонент / класс / модуль.
сверхлогичный
источник
3

Я не согласен :

В программной инженерии «рефакторинг» исходного кода означает его улучшение без изменения общих результатов [...]

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

l0b0
источник
1

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

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

Если вы реорганизуете класс, исправьте его общедоступный API и используя переменные переименования, методы извлечения и все другие доступные методы, чтобы сделать класс более читаемым и / или более производительным.

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

Борис Павлович
источник
0

Рефакторинг = улучшение нефункциональных требований при сохранении неизменных функциональных.

Нефункциональные требования = модульность, тестируемость, ремонтопригодность, удобочитаемость, разделение проблем, лисковские принципы и так далее ...

EnzoVici
источник