Як передавати параметри командного рядка до різних частин програми

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

Ці частини можуть бути ієрархічними, наприклад, main() екземпляри A (які створюють екземпляри B, C) і D (що породжує E), і кожен з них має специфічні вимоги до параметрів командного рядка. Існують різні можливості.

  • Зробіть глобальні змінні для параметрів, встановіть їх, а потім просто прочитайте їх за потребою. Можливо, у просторі назв конфігурації або класу або деякого роду.
  • Зробити HashMap, який відображає аргументи командного рядка, що входять до складу Enum, до їх значень. Це, звичайно, рядки, але кожна частина програми читає та інтерпретує типи, які вони вимагають. Ця карта передається ієрархічно з кожною реалізацією.
  • Зробіть класи для AConf, DConf, які потім заповнюються основним і передаються до A, і D, відповідно, з яких вони роблять BConf, CConf, EConf і передають ці об'єкти налаштування B, C, E, відповідно

і багато іншого.

У великому додатку з різними частинами, як це обробляється витончено?

Note: I'm not asking about argument parsing. I'm asking how to send the values to different objects after they've been parsed.

2
Це дуже широке, і для цього не існує єдиного рішення. Це має значення, якщо у вас є 5, 50 або 500 різних параметрів командного рядка, і це робить різницю в тому, що робить програма, як вона деталізована. Всі 3 ваші рішення можуть бути розумними, залежить від конкретних вимог.
додано Автор Peter LeFanu Lumsdaine, джерело

5 Відповіді

Для цього є нормально мати статичний клас у спільній бібліотеці, яка отримує весь командний рядок при запуску. Скажімо, ви називаєте його CommіLine. Потім можна створити подібні методи

bool HasSwitch(string)

і

string GetNamedArgument(string)

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

4
додано
Це рекомендується, оскільки аргументи командного рядка будуть статичним значенням для всього виконання! Незалежно від того, скільки потоків і класів ви будете породити, все всередині одного і того ж Процесу поділять ті ж самі аргументи - і аргументи незмінні!
додано Автор Falco, джерело

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

У моєму розумінні, інформація про конфігурацію витягується з (найвищий пріоритет):

  • аргументи командного рядка
  • Конфігураційний файл, наданий в аргументах командного рядка
  • змінні середовища
  • користувацький конфігураційний файл (наприклад: ~/.myapprc у Unix)
  • глобальний конфігураційний файл (наприклад: /etc/myapprc у Unix)
  • стандартна конфігурація, визначена під час компіляції
2
додано
Так, це можлива реалізація: глобальний або одиночний об'єкт Configuration , який збирає вхідні параметри з попередньо визначених джерел.
додано Автор gerrit, джерело
Так, у вашому рішенні, наприклад, Configuration буде єдиний клас з функцією read_conf , яка буде читати конфігурацію з різних місць і встановлювати її змінні для екземплярів, а потім Об'єкт Configuration буде передано по програмі?
додано Автор forumulator, джерело

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

Проблема починається з цього речення:

Основні() екземпляри A (які створюють екземпляри B, C) і D (що породжує E)

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

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

2
додано
Не всі об'єкти розташовані зверху, для об'єктів, інстальованих в іншому місці, можна створювати фабрику з вже наявними параметрами і т.д. Деякі параметри можна навіть передавати. Крім того, неважливо, наскільки «великим» є ваше додаток. Зовсім. Ви повинні використовувати декомпозицію , щоб прийти до керованих фрагментів/модулів/будь-якого, так що у верхній частині дійсно велика програма може взагалі не відрізнятися від невеликого додатка.
додано Автор Robert Bräutigam, джерело
Я розумію інверсію залежностей, але чи означає це, що всі об'єкти програми повинні бути реалізовані за допомогою main? Це не здається прийнятним для великих програм. Якщо у вас є великі частини програми, наприклад, шар даних, рівень повідомлень, рівень авторів, то кожен з цих шарів може мати дуже багато дрібних об'єктів, які не можуть бути створені за допомогою main. Як щодо цього?
додано Автор forumulator, джерело

У великому додатку з різними частинами, як це виконується витончено?

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

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

1
додано

Парадигма параметрів-моделей-перегляду-контролера

У тій же ситуації: багато випадків використання, використання кейсів в навігаційному меню, параметри до випадків використання (вибір деякої категорії, вибір пункту замовлення, ...).

We had a classical UseCase class, but now have a

UseCase
UseCase

Тому ми фактично розділили класичну модель на:

  • M (змінна) модель даних і
  • P (фіксована параметризація, змінні параметри/константи, дані заголовка)

Дійсно, параметри мають інше життя, ніж модель.

Це означає, що кожен випадок використання XxxUseCase отримав свій власний клас XxxParameters. Можна дуже стверджувати про якийсь сценарій/декларативний підхід; як згадка про хеш-карти. Але якщо немає потреби, посилання з одного компонента системи на інший найкраще обслуговується класом безпечного типу, зазначеного параметра. Особливо , якщо кількість та варіація параметрів зростають.

0
додано