Что делать с исходным файлом 11000 lines C++?


Итак, у нас есть этот огромный (это 11000 строк огромный?) mainmodule.исходный файл cpp в нашем проекте и каждый раз, когда я прикоснуться к нему, меня коробит.

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

файл и активно изменяется в несколько (> 10) версий обслуживанию нашей продукции и так очень тяжело отрефакторить его. Если бы я" просто " разделил его, скажем для начала, в 3 файла, а затем слияние обратно изменения из версий обслуживания станет кошмаром. А также если вы разбили файл с такой длинной и богатой историей, отслеживая и проверяя старые изменения в SCC история вдруг становится намного сложнее.

файл в основном содержит "основной класс" (main internal work dispatching and coordination) нашей программы, поэтому каждый раз, когда добавляется функция, она также влияет на этот файл и каждый раз, когда он растет. : - (

что как бы вы поступили в этой ситуации? Любые идеи о том, как переместить новые функции в отдельный исходный файл, не испортив SCC рабочий процесс?

(примечание по инструментам: мы используем C++ с Visual Studio; мы используем AccuRev как SCC но я думаю, что типа SCC на самом деле не имеет значения здесь; мы используем Araxis Merge чтобы сделать фактическое сравнение и слияние файлов)

30 222

30 ответов:

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

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

Итак, мы избавились от кода, который везде одинаков, и кода, который специфичен для определенных ветвей.

это оставляет вас с ядром плохо управляемых код-он нужен везде, но он отличается в каждой ветви (и/или он постоянно меняется, так что некоторые ветви работают за другими), и все же это в одном файле, который вы безуспешно пытаетесь объединить между ветвями. Прекрати это делать. Филиал файлом постоянно, возможно, переименовав его в каждой ветке. Это уже не" главное", это"главное для конфигурации X". Итак, вы теряете возможность применить одно и то же изменение к нескольким ветвям путем слияния, но это в любом случае ядро кода, где слияние работает не очень хорошо. Если вам все равно приходится вручную управлять слияниями, чтобы справиться с конфликтами, то нет никаких потерь вручную применять их независимо от каждой ветви.

Я думаю, вы ошибаетесь, говоря, что вид SCC не имеет значения, потому что, например, способности слияния git, вероятно, лучше, чем инструмент слияния, который вы используете. Таким образом, основная проблема "слияние сложно" возникает в разное время для разных SCCs. Однако, вы вряд ли сможете изменить SCCs, поэтому проблема, вероятно, не имеет значения.

слияние не будет таким большим кошмаром, как это будет, когда вы получите 30000 LOC-файл в будущем. Итак:

  1. прекратить добавлять больше кода в этот файл.
  2. разделить его.

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

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

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

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

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

в любом случае, это звучит как крупный проект, так что удачи :)

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

вставьте новый класс cpp выше исходного основного класса. На данный момент он в основном перенаправляет все вызовы на текущий основной класс, но стремится сделать API этого нового класса максимально ясным и кратким.

Как только это будет сделано, вы получите возможность добавления новых функций в новые классы.

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

Я знаю, что это не идеально, хотя я надеюсь, что это может помочь, и процесс должен быть адаптирован к вашим потребностям!

дополнительные информация

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

Git построен вокруг понятия blobs, которые, если я правильно понимаю, представляют собой фрагменты файлов кода. Перемещать эти части в разные файлы и Git найдет их, даже если вы их не измените. Кроме видео от Линуса Торвальдса упоминалось в комментариях ниже, я не смог найти что-то понятно об этом.

Конфуций говорит: "первый шаг к выходу из ямы-это прекратить рыть яму."

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

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

прежде чем пытаться разбить файл на части, необходимо приведите десять ветвей обратно в синхронизацию друг с другом, чтобы вы могли видеть и формировать весь код сразу. Вы можете делать это по одной ветви за раз, тестируя обе ветви против одного и того же файла основного кода. Для обеспечения пользовательского поведения вы можете использовать #ifdef и friends, но лучше как можно больше использовать обычный if/else против определенных констант. Таким образом, ваш компилятор проверит все типы и, скорее всего, устранит "мертвый" объектный код в любом случае. (Вы можете отключить предупреждение о мертвых код, однако.)

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

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

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

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

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

Я думаю, что у вас есть только такие варианты:

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

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

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

именно эта проблема рассматривается в одной из глав книги "эффективная работа с устаревшим кодом" (http://www.amazon.com/Working-Effectively-Legacy-Michael-Feathers/dp/0131177052).

Я думаю, что вам было бы лучше создать набор команда классы, которые сопоставляются с точками API mainmodule.СРР.

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

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

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

обновление

Я бы предположил, что шаблон команды предпочтительнее фасада.

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

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

конечно, оба варианта лучше, чем один 11 клок и растет, файл.

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

один из способов может заключаться в том, чтобы начать разбиение наименее большой функции/части на собственный файл, а затем либо включить с заголовком (таким образом, превратив main.cpp в список #includes, который звучит как запах кода сам по себе *я не гуру C++), но, по крайней мере, теперь он разделен в файлы.)

затем вы можете попытаться переключить все релизы обслуживания на" новый " main.cpp или независимо от вашей структуры. Опять же: никаких других изменений или исправлений, потому что отслеживание их сбивает с толку, как ад.

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

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

рофл, это напоминает мне о моей старой работе. Кажется, что до того, как я присоединился, все было внутри одного огромного файла (также C++). Затем они разделили его (в совершенно случайных точках, используя includes) примерно на три (все еще огромные файлы). Качество этого программного обеспечения было, как и следовало ожидать, ужасным. Проект составил около 40к Лок. (почти без комментариев, но много дубликатов кода)

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

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


edit:

размер остался примерно на 40k LOC, но новое приложение содержало гораздо больше функций и, по-видимому, меньше ошибок в своей первоначальной версии, чем 8-летнее программное обеспечение. Одной из причин переписывания было также то, что нам нужны были новые функции, и введение их в старый код было почти невозможно.

программное обеспечение было для встроенной системы, принтер этикеток.

еще один момент, который я должен добавить, что в теории проект был на C++. Но это был вовсе не OO, это мог быть C. новая версия была объектно-ориентированной.

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

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

во-вторых, вам нужно взять эту производственную версию и создать ветвь, которая управляет сборками с помощью директив предварительной обработки для разделения большого файла. Разделение компиляции с использованием только директив препроцессора (#ifdefs, #includes, #endifs) проще, чем перекодировать API. Это определенно проще для ваших SLA и постоянной поддержки.

здесь вы можете просто вырезать функции, которые относятся к определенной подсистеме в классе и поместить их в файл, скажем mainloop_foostuff.cpp и включить его в mainloop.cpp в нужном месте.

или

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

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

основная структура заключается в том, что ваш mainclass.cpp будет включать в себя новый файл под названием MainClassComponents.cpp после блока операторов, таких как следующее:

#if VARIANT == 1
#  define Uses_Component_1
#  define Uses_Component_2
#elif VARIANT == 2
#  define Uses_Component_1
#  define Uses_Component_3
#  define Uses_Component_6
...

#endif

#include "MainClassComponents.cpp"

первичная структура компонентов mainClass.файл cpp будет работать вне зависимости составляющих такой:

#ifndef _MainClassComponents_cpp
#define _MainClassComponents_cpp

/* dependencies declarations */

#if defined(Activate_Component_1) 
#define _REQUIRES_COMPONENT_1
#define _REQUIRES_COMPONENT_3 /* you also need component 3 for component 1 */
#endif

#if defined(Activate_Component_2)
#define _REQUIRES_COMPONENT_2
#define _REQUIRES_COMPONENT_15 /* you also need component 15 for this component  */
#endif

/* later on in the header */

#ifdef _REQUIRES_COMPONENT_1
#include "component_1.cpp"
#endif

#ifdef _REQUIRES_COMPONENT_2
#include "component_2.cpp"
#endif

#ifdef _REQUIRES_COMPONENT_3
#include "component_3.cpp"
#endif


#endif /* _MainClassComponents_h  */

и теперь для каждого компонента вы создаете component_xx.cpp файл.

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

использование препроцессора позволяет вам разделить вещи, не беспокоясь об изменениях API, которые являются кошмар в производстве.

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

Ну я понимаю вашу боль :) я тоже был в нескольких таких проектах, и это не очень красиво. На это нет простого ответа.

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

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

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

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

Это не ответ на большую проблему, а теоретическое решение конкретной ее части:

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

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

  • запустить скрипт. Удалите исходный файл.

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

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

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

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

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

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

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

  1. напишите код для исчерпывающего тестирования функции в рассматриваемой программе. Похоже, у тебя этого еще не будет...
  2. определите некоторый код, который может быть абстрагирован в класс helper/utilities. Не нужно быть большим, просто то, что на самом деле не является частью вашего "главного" класс.
  3. рефакторинг кода, указанного в 2. в отдельный класс.
  4. повторите свои тесты, чтобы убедиться, что ничего не сломалось.
  5. когда у вас есть время, Гото 2. и повторите по мере необходимости, чтобы сделать код управляемым.

классы, которые вы строите в шаге 3. итерации, вероятно, будут расти, чтобы поглотить больше кода, который подходит для их недавно очищенной функции.

Я мог бы также добавить:

0: Купить Майкл Перья' книга при работе с устаревшим кодом

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

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

Если вы определили работоспособное решение, выполните рефакторинг приложения соответствующим образом.

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

мои 0,05 евроцента:

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

при разбиении на подсистемы проанализируйте наиболее измененные места и отделите их от неизменных частей. Это должно показать вам проблемные места. Отдельные наиболее изменение частей на свои собственные модули (например, dll) таким образом, что API модуля может быть сохранен в целости и сохранности, и вам не нужно все время ломать BC. Таким образом, вы можете развернуть различные версии модуля для различных ветвей обслуживания, если это необходимо, при неизменном ядре.

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

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

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

вот как я бы начал решать эту проблему на наименьший.

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

еще одна книга, которую вы можете найти интересной / полезной, это рефакторинг.

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

Я нашел это предложение самой интересной частью вашего поста:

> файл используется и активно изменяется в несколько (> 10) ведение версий нашего продукта и поэтому его очень тяжело отрефакторить его

во-первых, я бы рекомендовал вам использовать систему управления версиями для разработки этих версий обслуживания 10+, которая поддерживает ветвление.

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

Я уже чувствую, как ты съеживаешься! Но либо ваша система управления версиями не работает для вашей ситуации из-за отсутствия функций, либо она используется неправильно.

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

Я бы немного обеспокоен тем, что у тебя столько в функции main ().

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

Я бы также инициализировал объект регистрации приложения в main для использования глобально во всей программе.

наконец, в main я также добавляю код обнаружения утечек в блоки препроцессора, которые гарантируют, что он включен только в отладочных сборках. Это все, что я хотел бы добавить к main(). Главное () должно быть коротким!

вы говорите, что

> файл в основном содержит "основной класс" (основная внутренняя работа диспетчеризации и координации) нашей программы

похоже, эти две задачи можно разделить на два отдельных объекта - координатора и диспетчера.

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

Если вы не в состоянии принять решение, бороться зубами и ногтями с вашим менеджером для Он - приложении должен быть переработан и плохо слышно! Не принимайте " нет " за ответ!

Как вы уже описали, основная проблема заключается в различии pre-split vs post-split, слиянии в исправлениях ошибок и т. д.. Инструмент вокруг него. Это не займет много времени, чтобы жестко закодировать скрипт в Perl, Ruby и т. д. чтобы вырвать большую часть шума от диффузии предварительного разделения против конкатенации пост-разделения. Делайте все, что проще всего с точки зрения обработки шума:

  • удалить некоторые линии до / во время конкатенации (например, включить защиту)
  • удалите другие вещи из вывода diff, если необходимо

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

  1. никогда не прикасайтесь к этому файлу и коду снова!
  2. лечить, как то,что вы застряли. Начните писать адаптеры для функций, закодированных там.
  3. напишите новый код в разных блоках и говорите только с адаптерами, которые инкапсулируют функциональность монстра.
  4. ... если только одно из вышеперечисленного невозможно, бросьте работу и получите новую.

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

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

    // declaration
    std::map<ID, ICommand*> dispatchTable;
    ...

    // populating using some loader
    dispatchTable[id] = concreteCommand;

    ...
    // using
    dispatchTable[id]->Execute();

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

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

Я думаю, что я бы сделал в этой ситуации немного пуля и:

  1. выясните, как я хотел разделить файл (на основе текущей версии разработки)
  2. поместите административную блокировку на файл ("никто не трогает mainmodule.cpp после 5 вечера пятницы!!!"
  3. проведите свои длинные выходные, применяя это изменение к > 10 версиям обслуживания (от самой старой до самой новой), вплоть до текущей версии и включая ее.
  4. удалить mainmodule.cpp из все поддерживаемые версии программного обеспечения. Это новый век - нет больше mainmodule.СРР.
  5. убедите руководство, что вы не должны поддерживать более одной версии программного обеспечения для обслуживания (по крайней мере, без большого контракта на поддержку$$$). Если у каждого из ваших клиентов есть своя уникальная версия.... yeeeeeshhhh. Я бы добавил директивы компилятора, а не пытался поддерживать 10+ вилки.

отслеживание старых изменений в файле просто решается вашим первым Регистрация комментарий говорит что-то вроде "сплит от mainmodule.СРР." Если вам нужно вернуться к чему-то недавнему, большинство людей будут помнить об изменениях, если это будет через 2 года, комментарий скажет им, где искать. Конечно, насколько ценно будет вернуться более чем на 2 года назад, чтобы посмотреть, кто изменил код и почему?