Где вы должны положить константы и почему?

34

В наших в основном больших приложениях у нас обычно есть только несколько мест для «констант»:

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

Константы обычно разделяются на разные структуры в этих классах. В наших приложениях C ++ константы определяются только в файле .h, а значения назначаются в файле .cpp.

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

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

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

Однако я вижу определенные недостатки:

  • Каждый отдельный класс тесно связан с классами констант
  • Добавление / удаление / переименование / перемещение константы требует перекомпиляции как минимум 90% приложения (Примечание: изменение значения не происходит, по крайней мере для C ++). В одном из наших проектов C ++ с 1500 классами это означает около 7 минут времени компиляции (с использованием предварительно скомпилированных заголовков; без них - около 50 минут) плюс около 10 минут соединения с определенными статическими библиотеками.
  • Создание оптимизированного по скорости выпуска с помощью компилятора Visual Studio занимает до 3 часов. Я не знаю, является ли огромное количество классовых отношений источником, но это может быть так.
  • Вы попадаете в строки временного жесткого кодирования прямо в код, потому что вы хотите очень быстро что-то тестировать и не хотите ждать 15 минут только для этого теста (и, вероятно, каждого последующего). Все знают, что происходит с мыслями «Я исправлю это позже».
  • Повторное использование класса в другом проекте не всегда так просто (в основном из-за других жестких связей, но обработка констант не делает это проще).

Где бы вы хранили такие константы? Кроме того, какие аргументы вы бы привели, чтобы убедить своего менеджера проекта, что есть лучшие концепции, которые также соответствуют перечисленным выше преимуществам?

Не стесняйтесь давать C ++ - конкретный или независимый ответ.

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

Обновленная информация об этом проекте

У меня есть новости о времени компиляции:
следуя сообщениям Caleb и gbjbaanb, я разбил свой файл констант на несколько других файлов, когда у меня было время. Я также со временем разделил свой проект на несколько библиотек, что стало теперь намного проще. Компиляция этого в режиме выпуска показала, что автоматически сгенерированный файл, который содержит определения базы данных (таблицы, имена столбцов и более - более 8000 символов) и создает определенные хэши, вызвал огромное время компиляции в режиме выпуска.

Отключение оптимизатора MSVC для библиотеки, которая содержит константы БД, теперь позволило нам сократить общее время компиляции вашего Проекта (нескольких приложений) в режиме выпуска с 8 часов до менее одного часа!

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

Этот факт - и другие преимущества, такие как менее тесная связь, лучшая возможность повторного использования и т. Д. - также показали, что тратить время на разделение «констант» не так уж и плохо ;-)

Update2

Поскольку этому вопросу все еще уделяется некоторое внимание:
вот что я делал в последние несколько лет:

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

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

Обратите особое внимание на Принцип Интерфейса Разделения Интерфейса и Принцип Единой Ответственности, если вы хотите узнать больше.

Если вы согласны, ответьте на вопрос Калеба, так как это обновление - в основном более общее представление о том, что он сказал.

Тим Мейер
источник
2
лично у меня вообще не было бы заголовка интерфейса пользователя или строк сообщения в константах. Я хотел бы иметь их в app.config
JK.
1
Мне нравится, как вы делаете это сейчас - я понимаю ваши недостатки, но нам, возможно, просто придется с этим справиться.
bigtang
1
Я видел точно такую ​​же проблему в большом Java-проекте ... Один огромный "константный" интерфейс, измените в нем что-нибудь и подождите 15 минут, чтобы затмение перекомпилировалось. Я с Caleb: сгруппируйте константы там, где они, естественно, близки к коду, который их использует. Хранить их отдельно, потому что они постоянные, - бесполезное упражнение ОКР.
Майкл Боргвардт
Тесная связь не является проблемой, потому что это то, что вы на самом деле хотите. Вы хотите, чтобы одно изменение в вашем файле констант влияло на множество исходных файлов. (Конечно, у вас есть и другие проблемы).
gnasher729
@ gnasher729, это верно только в том случае, если многие классы используют одну и ту же константу. Вы бы никогда не хотели, чтобы классы были тесно связаны с константами, с которыми они не связаны. На первый взгляд это может не показаться проблемой, пока вы не попытаетесь использовать его в другом проекте, не скопировав его или не запустив изолированные тесты
Тим Мейер

Ответы:

29

Константы, которые являются определенными для класса, должны идти в интерфейсе этого класса.

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

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

Калеб
источник
1
Если вам интересно: я обновил свой вопрос тем, чего достиг, после вашего ответа и других
Тим Мейер
6

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

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

gbjbaanb
источник
Я мог бы дать этому шанс. Поскольку класс констант с заголовками и т. Д. Разбит на несколько структур, я мог бы, возможно, сделать один класс на структуру в качестве начала. Не уверен насчет GNU, мы обычно не хотим менять наши строки во время выполнения, просто во время разработки. Однако мы используем механизм перевода Qt на случай, если в будущем нам придется переводить на другой язык.
Тим Мейер
Если вам интересно: я обновил свой вопрос тем, чего я достиг, следуя вашему и другим ответам
Тим Мейер
2

Примечание: я не разработчик на C ++ ... но вот моя мысль: вам нужно учесть следующий комментарий @ jk о разнице между использованием файлов конфигурации. В DotNet есть файл ресурсов, который используется для хранения такой информации. В Windows Forms файл ресурсов поддерживается из VS для каждой формы.

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

Теперь, если вы хотите, чтобы не программист просматривал информацию, тогда для GUI вы снимаете экран для них. Если вы хотите, чтобы они просматривали записи таблицы данных, вы можете экспортировать данные в Excel или что-то подобное.

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

Без шансов
источник
2

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

Чем ближе они определены к своему объему, тем выше производительность.

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

Чем менее доступны клиенты, тем выше безопасность.

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

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

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

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

Кристиан
источник
1

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

MainPanel.Title = Добро пожаловать в наше приложение
DB.table.users = TBL_USERS
logging.filename = application.log

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

Для реализации я нашел этот вопрос SO: https://stackoverflow.com/questions/874052/properties-file-library-for-c-or-c (это был первый хит в поиске Google - у меня нет на самом деле использовал это программное обеспечение сам).

FrustratedWithFormsDesigner
источник
Для проектов C ++ нам нужно только перекомпилировать файл констант, если мы изменим значение. Это связано с тем, что значения присваиваются в файле .cpp. Однако добавление / удаление / переименование / перемещение константы все еще требует полной перестройки
Тим Мейер
@TimMeyer: если вы разделите константы на несколько файлов, добавление / удаление константы повлияет только на файлы, которые зависят от этого конкретного файла, правильно?
FrustratedWithFormsDesigner
Правильный. Основная проблема заключается в том, что, если я предлагаю это, люди склонны упоминать одну из вещей, которые я перечислил как «преимущества», теряются
Тим Мейер
Однако на самом деле я не думал о том, чтобы поместить несколько файлов констант в одно и то же место
Тим Мейер
+1. @ Тим Мейер: Для этого проверка во время компиляции стоит намного дороже, чем экономит. Кроме того, что вы собираетесь делать, если вам нужно интернационализировать?
Кевин Клайн