Куди слід ставити константи і чому?


34

У наших переважно великих програмах ми зазвичай маємо лише кілька місць для "констант":

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

Константи зазвичай розділені на різні структури в цих класах. У наших додатках C ++ константи визначаються лише у файлі .h, а значення призначаються у файлі .cpp.

Однією з переваг є те, що всі рядки тощо знаходяться на одному центральному місці, і всі знають, де їх знайти, коли щось потрібно змінити.

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

Крім того, ви можете легко змінити заголовок подібних групових скриньок / сторінок вкладок тощо. Інший аспект полягає в тому, що ви можете просто роздрукувати цей клас і надати його непрограмісту, який може перевірити, чи підписи є інтуїтивними, а також якщо повідомлення користувачеві надто детальні чи занадто заплутані тощо.

Однак я бачу певні недоліки:

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

Де б ви зберігали такі константи? Також які аргументи ви б навели, щоб переконати свого керівника проекту в тому, що існують кращі концепції, які також відповідають переліченим вище перевагам?

Сміливо дайте відповідь на C ++ - конкретну чи незалежну.

PS: Я знаю, що це питання є певним суб'єктивним, але я, чесно кажучи, не знаю жодного кращого місця, ніж цей сайт для такого роду питань.

Оновлення цього проекту

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

Дезактивація оптимізатора MSVC для бібліотеки, яка містить константи БД, тепер дозволила нам скоротити загальний час компіляції вашого проекту (кілька програм) у режимі випуску з 8 годин до менш ніж однієї години!

Ми ще не з'ясували, чому MSVC настільки важко оптимізує ці файли, але наразі ця зміна знімає сильний тиск, оскільки нам більше не доводиться покладатися лише на нічні побудови.

Цей факт - а також інші переваги, такі як менш жорстке з'єднання, краща повторність використання тощо - також показали, що витрачати час на розбиття «констант» все-таки не така погана ідея ;-)

Оновлення2

Оскільки це питання все ще привертає певну увагу:
ось що я робив за останні кілька років:

Поставити кожну константу, змінну тощо точно в ту область, яка для неї є актуальною: Якщо ви використовуєте константу лише в одному методі, визначити її в цьому методі добре. Якщо один клас зацікавлений у ньому, залиште його як приватну детальну інформацію про цей клас. Те саме стосується простору імен, модуля, проекту, сфери діяльності компанії. Я також використовую ту саму схему для допоміжних функцій тощо. (Це може не застосовуватися на 100%, якщо ви розробляєте загальнодоступні рамки.)

Це збільшує багаторазове використання, тестуваність та ремонтопридатність до такої міри, коли ви не тільки витрачаєте менше часу на компіляцію (принаймні, на C ++), але і менше часу на виправлення помилок, що залишає вам більше часу для фактичного розвитку нових функцій. У той же час, розробка цих функцій піде швидше, оскільки ви можете використовувати більше коду легше. Це переважає будь-яку перевагу, яку може мати файл центральних констант на величину.

Подивіться особливо на принцип сегрегації інтерфейсу та принцип єдиної відповідальності, якщо хочете дізнатися більше.

Якщо ви згодні, підтримайте відповідь Калеба, оскільки це оновлення в основному є більш загальним питанням щодо того, що він сказав.


2
особисто я б взагалі не мав заголовків або рядків повідомлення в константах. Я мав би їх у app.config
jk.

1
Мені якось подобається, як ти це робиш зараз - я розумію твої недоліки, але ми можемо просто з цим впоратися.
bigtang

1
Я бачив точно таку ж проблему у великому проекті Java ... Один величезний "константний" інтерфейс, будь-що змінити в ньому і зачекати 15 хвилин, щоб затемнення перекомпілювати. Я з Калебом: згрупуйте константи там, де вони природно належать, близько до коду, який їх використовує. Тримати їх окремо, оскільки вони постійні, це марне вправу OCD.
Майкл Боргвардт

Щільна муфта не є проблемою, адже саме цього ви хочете. Ви хочете, щоб одна зміна файлу констант вплинула на, можливо, багато вихідних файлів. (Звичайно, у вас є й інші проблеми).
gnasher729

@ gnasher729, це справедливо лише в тому випадку, якщо в багатьох класах використовується одна і та ж константа. Ви ніколи не хочете, щоб заняття були щільно поєднані з константами, з якими вони не пов'язані. Спочатку це може здатися проблемою, поки ви не спробуєте повторно використовувати його в іншому проекті, не скопіювавши його чи не запустивши ізольовані тести
Тім Меєр,

Відповіді:


29

Константи, характерні для класу, повинні входити в інтерфейс цього класу.

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

Константи, які поділяються між класами, але не призначені для налаштування, повинні бути обґрунтованими. Спробуйте розбити їх на файли, які мають особливе використання, щоб окремі класи включали лише те, що їм потрібно. Це знову допоможе скоротити час компіляції, коли ви змінюєте деякі з цих констант.


1
У випадку, якщо ви зацікавлені: я оновив своє запитання щодо того, що досягнув, слідуючи вашої відповіді та інших
Тім Меєр

6

Я б сказав просто, що ви хочете розділити свій величезний клас констант на багато менших файлів, наприклад, один на форму. Це гарантує, що у вас немає такої великої залежності від константного файлу, тому для додавання або оновлення рядка не знадобиться повна перекомпіляція. Ви можете зберігати ці файли в центральному місці, але (наприклад, мати 1 файл із константами для кожного діалогового вікна, відповідним іменем. Тоді ви можете включити лише ті файли у відповідні діалогові файли, що масово скорочує рекомпіляцію.

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


Я можу це зробити. Оскільки клас констант із заголовками тощо розділений на кілька структур, я, можливо, можу зробити один клас на структуру як початок. Не впевнені в 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

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

Для реалізації я знайшов це ТАКЕ питання: https://stackoverflow.com/questions/874052/properties-file-library-for-c-or-c (це було першим зверненням до пошуку Google - я не фактично сам користувався цим програмним забезпеченням).


Для проектів C ++ нам потрібно лише перекомпілювати файл констант, лише якщо ми змінимо значення. Це тому, що значення призначаються у файлі .cpp. Однак додавання / видалення / перейменування / переміщення константи все ще потребує повної перебудови
Тім Мейєр,

@TimMeyer: Якщо ви розділите константи на кілька файлів, додавання / видалення константи вплине лише на файли, які залежать від конкретного файлу, правильно?
FrustratedWithFormsDesigner

Правильно. Основна проблема полягає в тому, що якщо я це припускаю, люди, як правило, згадують одну з речей, які я перерахував як "переваги", втрачаються
Тім Мейер

Однак я насправді не замислювався над тим, щоб розміщувати кілька файлів констант в одному місці
Тім Меєр,

+1. @Tim Meyer: Для цього перевірка часу компіляції коштує набагато більше, ніж економить. Крім того, що ви збираєтеся робити, якщо вам потрібно інтернаціоналізувати?
Кевін Клайн
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.