Как вы реорганизуете класс, который постоянно редактируется? - PullRequest
10 голосов
/ 25 февраля 2009

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

Мандат истек. Нам нужно провести рефакторинг класса монстров. Самая большая часть плана состоит в том, чтобы определить категории функциональности в их собственных классах с отношениями «имеет-связь» с классом монстров.

Это означает, что многие ссылки, которые в настоящее время читаются так:

var monster = new orMonster();
var timeToOpen = monster.OpeningTime.Subtract(DateTime.Now);

скоро будет читать так:

var monster = new Monster();
var timeToOpen = monster.TimeKeeper.OpeningTime.Subtract(DateTime.Now);

Вопрос в том, как на Земле мы координируем такое изменение? Ссылки на «orMonster» замусоривают каждый бизнес-класс. Некоторые методы вызываются буквально тысячами мест в коде. Гарантируется, что в любой момент, когда у нас будет такая возможность, кто-то еще (возможно, несколько человек) в команде получит проверенный код, который вызывает свойство .OpeningTime

Как вы координируете такое крупномасштабное изменение без остановки производительности?

Ответы [ 12 ]

27 голосов
/ 25 февраля 2009

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

Для получения дополнительной информации см. Метод перемещения в классике Мартина Фаулера, Рефакторинг .

10 голосов
/ 25 февраля 2009

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

7 голосов
/ 25 февраля 2009

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

6 голосов
/ 25 февраля 2009

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

http://dotnettipoftheday.org/tips/ObsoleteAttribute.aspx

4 голосов
/ 25 февраля 2009

Развивайте свои изменения в ветке. Разбейте подмножество кода на новый класс, внесите изменения в клиентскую базу, тщательно протестируйте и затем объедините обратно.

Это концентрирует разрыв, когда вы сливаетесь, а не весь цикл разработки.

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

Я также повторяю совет нескольких авторов, чтобы разоблачить разбитые классы напрямую, а не через монстра. Зачем применять только половину лечения? С таким же усилием вы можете применить полное излечение.

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

3 голосов
/ 25 февраля 2009

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

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

2 голосов
/ 25 февраля 2009
var monster = new Monster();
var timeToOpen = monster.TimeKeeper.OpeningTime.Subtract(DateTime.Now);

Я не уверен, что лучше разделить его и просто сделать его общедоступным. Это нарушает закон Деметры и может привести к боли NullReference.

Я бы предложил разоблачить хронометриста людям без привлечения монстра.

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

2 голосов
/ 25 февраля 2009

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

1 голос
/ 17 марта 2009

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

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

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

1 голос
/ 25 февраля 2009

Несколько человек дали хорошие ответы относительно организации самого рефактора. Это ключ. Но вы также спросили о координации изменений между несколькими людьми (что, я думаю, было сутью вашего вопроса). Какой источник контроля вы используете? Что-нибудь вроде CVS, SVN и т. Д. Может обрабатывать входящие изменения от нескольких разработчиков одновременно. Ключ к тому, чтобы все прошло гладко, заключается в том, что каждый человек должен делать свои коммиты детальными и атомарными, а каждый разработчик должен часто делать коммиты других людей.

...