Стратегия масштабного рефакторинга
В настоящее время я работаю над фрагментом кода, в котором как логика, так и доступ к данным присутствуют в классах GUI. Очевидно, я хотел бы улучшить эту ситуацию.
Текущая структура тока в основном:
Конечной целью является создание DDD-подобной структуры:
- DAL
- Модель домена
- Уровень обслуживания
- Модель представления
- GUI
Итак, как бы вы атаковали проблему?
- Большой взрыв
- Определите структуру для конечного состояния и нажмите код в своем конечном доме.
- Разделите и победите
- Попробуйте отделить большой шар грязи до двух частей. Повторяйте до конца...
- удушающий
Ответы
Ответ 1
Никогда не пытайтесь "Большой взрыв". Это почти всегда ударит вам в лицо, поскольку это очень рискованная, отчаянная мера, когда все остальное не удалось.
Разделите и победите: это хорошо работает... если ваш мир имеет только две стороны. В реальном программном обеспечении вы должны одновременно завоевать так много фронтов, вы редко можете позволить себе жить в черно-белой фантазии.
Наверное, я использовал что-то вроде "Удушения" на протяжении большей части своей карьеры: постепенно превращал плохой старый код в блестящий новый код. Вот мой рецепт:
Начните с чего-то, на самом деле это не имеет значения. Напишите несколько модульных тестов, чтобы увидеть, как действительно работает код. Узнайте, как часто он делает то, что, по вашему мнению, делает, и как часто это не так. Используйте свою IDE для рефакторинга кода, чтобы вы могли проверить его.
После первого дня угадайте, начали ли вы в нужном месте, чтобы разбить этого монстра. Если да, продолжайте. Если нет, найдите новое место и начните.
Преимущества этой стратегии: она работает небольшими шагами, поэтому риск можно держать под контролем, и если что-то ломается, если должно быть в коде, который вы работали на прошлой неделе.
Недостаток: вам нужно много времени, и вы почувствуете разочарование, потому что часто прогресс будет казаться настолько медленным, пока "узел" не появится, и внезапно все начинает падать, как по волшебству.
Ответ 2
Я никогда не слышал о терминах "Приложение Strangler" - мне это нравится. Там, где это возможно, это всегда было бы хорошим подходом, это, безусловно, сводит к минимуму риск и является довольно прагматичным, отрываясь от большого здания по частям.
В тех случаях, когда это не работает в моем опыте, необходимо немедленно внести существенные изменения - изменения, которые потребуют немного рефакторинга (или большого взлома). В этой ситуации я часто обнаружил, что изменения, которые я должен был сделать, были в центре большого шара грязи, и не было никакого выбора, кроме как стать грязным - даже то, что должно было быть стандартным обслуживанием или незначительными изменениями в улучшении, было просто ужасно и главный рефактор был лучшим вариантом.
В этих случаях я бы пошел с разделом и победой - первая цель, к которой я всегда стремилась, - это тестируемость, когда у вас есть все, что намного проще. На самом деле, это часто является одним из основных драйверов, которые у меня есть для рефакторинга вдали от большого шара грязи. Такой код часто очень не поддается тестированию, надеюсь, что есть пример входов и выходов пользовательского интерфейса, но иногда даже это отсутствует.
Поэтому, столкнувшись с кодом, в котором все сосредоточено в пользовательском интерфейсе, я обычно начинаю с факторизации отдельных единиц функциональности в классы и методы, а затем перетаскивая эти части кода в домен или уровень обслуживания. Выполнение этого по частям значительно снижает вероятность взлома чего-то и упрощает привязку к месту, где был взломан код, когда дела идут не так.
Запустите все тестовые примеры, которые у вас есть в конце каждого изменения, и убедитесь, что вы все еще встречаете какую-то базовую линию.
Если вы пишете хорошие модульные тесты по мере того, как вы идете, вы можете начать уменьшать масштаб проблемы, и я обнаружил, что вскоре становится практичным принимать подход душителя - с достойными модульными тестами или, по крайней мере, с правильной структурой, позволяющей написание достойных модульных тестов становится гораздо более практичным, чтобы постепенно заменить части функциональности.
Ответ 3
Я наткнулся на "Метод Микадо", который кажется многообещающим для атаки на такие проблемы.
http://mikadomethod.wordpress.com/
Существует также разговор о методе Микадо от Øredev 2010.
http://oredev.org/2010/sessions/large-scale-refactorings-using-the-mikado-method
Ответ 4
В зависимости от того, должно ли вы иметь всегда рабочее состояние, чтобы вы могли исправлять ошибки и развертывать их каждый раз при необходимости, то Devide и Conquer были бы хорошим решением. Если вы можете сохранить старый код, работая над новым (и попросите ученика применить исправления ошибок к кодам), переписывание может быть лучшим решением.
Ответ 5
Если при рефакторинге вы имеете в виду улучшение кода без изменения функциональности, я бы начал с создания базовой линии автоматического регрессионного тестирования. Есть много инструментов, чтобы помочь с этим. Я использую TestComlete, хотя есть хорошие дешевые альтернативы.
Установив базовый уровень регрессионного теста, лично я бы пошел с разделом и победой, так как по моему опыту он, скорее всего, преуспеет. После того, как у вас есть базовый уровень тестирования, это менее важно, какой подход вы выбираете.
Ответ 6
Для меня это зависит от ситуации.
Если это очень маленький проект, у меня возникнет соблазн просто переписать его с нуля... однако вы не часто пользуетесь этой роскошью.
В противном случае я пошлю его по частям. Я бы написал модульные тесты, чтобы проверить существующие функции и медленно использовать TDD, чтобы преобразовать код в элегантную и хорошо продуманную систему. В зависимости от того, как долго этот процесс будет проходить, он, вероятно, начнет выглядеть как упоминавшееся выше StranglerApplication.
BigBang очень рискован, так как у вас нет простого способа проверить, что обновленная система выполняет то же самое, что и старое.
Divide и Conquer менее рискованны, чем BigBang... но если его достаточно большая система, это может оказаться столь же проблематичным, как BigBang.
Ответ 7
Big bang/Big re-design/rewriting SW... или любые другие имена не будут работать для жизни SW.
Причины таковы:
-
Вам все равно нужно поддерживать существующий SW (возможно) теми же ресурсами, которые у вас есть.
-
У вас, вероятно, нет требований к перезаписи. У вашего старого кода есть все требования, встроенные в него. Ни один из ваших инженеров не знает всех доменов SW и всех требований.
-
Перезапись займет время. По истечении этого времени вы обнаружите, что существующее ПО изменилось, чтобы поддерживать то, что требовалось в течение этого времени.
ваш новый SW действительно раскололся бы от оригинала, и потребуется слияние (что также потребует времени).
Ответ 8
Можно ли переписать вариант? По моему опыту переписывание с нуля часто может быть более эффективным, чем попытка очистить существующий беспорядок. У вас холод по-прежнему сохраняются части существующего кода, но в новом контексте. И то же самое касается gui и базы данных, если у вас есть. Перепишите с нуля и возьмите с собой то, что вы можете использовать.
Ответ 9
Начиная с чистой новой архитектуры и перемещая старые фрагменты кода в эту новую архитектуру по частям и рефакторинг ее в соответствии с новой дугой, будет хорошим вариантом. Я думаю, что подход с восходящим потоком при перемещении функций будет хорошим.