C++: как передать пользовательский ввод через систему без использования глобальных переменных?

У меня проблема в том, что мое приложение может иметь много пользовательского ввода, который определяет, как приложение будет запущено. Приложение представляет собой систему базы данных в памяти, и пользователь может, например, вызвать программу с помощью команд, таких как '--pagesize 16384' (устанавливает размер страницы памяти для использования), '--alignment 4096' (устанавливает выравнивание памяти для использования) или --memeasure (устанавливает флаг для измерения определенных подпрограмм).

В настоящее время я сохраняю весь пользовательский ввод в глобальных переменных, которые определены как extern в заголовочном файле:

//@file common.hh
extern  size_t      PAGE_SIZE_GLOBAL;
extern  size_t      ALIGNMENT_GLOBAL;
extern  size_t      MEMCHUNK_SIZE_GLOBAL;
extern  size_t      RUNS_GLOBAL;
extern  size_t      VECTORIZE_SIZE_GLOBAL;
extern  bool        MEASURE_GLOBAL;
extern  bool        PRINT_GLOBAL;
extern  const char* PATH_GLOBAL;

и в основном исходном файле:

#include "modes.hh"

size_t      PAGE_SIZE_GLOBAL;
size_t      ALIGNMENT_GLOBAL;
size_t      MEMCHUNK_SIZE_GLOBAL;
size_t      RUNS_GLOBAL;
size_t      VECTORIZE_SIZE_GLOBAL;
bool        MEASURE_GLOBAL;
bool        PRINT_GLOBAL;
const char* PATH_GLOBAL;

int main(const int argc, const char* argv[]){

    ...
    //Initialize the globals with user input
    PAGE_SIZE_GLOBAL        = lArgs.pageSize();
    ALIGNMENT_GLOBAL        = lArgs.alignment();
    MEMCHUNK_SIZE_GLOBAL    = lArgs.chunkSize();
    RUNS_GLOBAL             = lArgs.runs();
    VECTORIZE_SIZE_GLOBAL   = lArgs.vectorized();
    MEASURE_GLOBAL          = lArgs.measure();
    PRINT_GLOBAL            = lArgs.print();
    std::string tmp         = lArgs.path() + storageModel + "/";
    PATH_GLOBAL             = tmp.c_str();

    ...
}

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

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

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

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

(Отредактировано) Я также подумал об использовании глобального класса данных Singleton. Но объект со ВСЕМИ глобальными данными звучит излишне, поскольку в его разных местах назначения требуется всего несколько глобальных переменных объекта.

Мой вопрос: я должен просто придерживаться глобальных переменных? Есть ли лучшие решения, чего мне не хватает? Каковы лучшие практики?

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

1 ответ

Решение

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

Это легко. Сюрприз, я создал класс.

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

Этот TC_t создается в потоке main() при изучении параметров командной строки.


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

GOLUtil_t (Game Of Life Utility) (ранее TC_t) включает методы, которые полезны более чем в одном приложении.

По вашему вопросу, два типичных глобала, которых я избегал, - это а) gameBoard и б) доступ к терминалу ANSI.

std::cout << "accessing '" << aTermPFN << "' with std::ofstream " 
          << std::endl;
std::ofstream*  ansiTerm = new std::ofstream(aTermPFN);

if (!ansiTerm->is_open())
{
   dtbAssert(nullptr != ansiTerm)(aTermPFN);
   std::cerr << "Can not access '" << aTermPFN << "'" << std::endl;
   assert(0);  // abort
 }

// create game-board - with a vector of cell*
CellVec_t  gameBoard;
gameBoard.reserve (aMaxRow * aMaxCol);

GOLUtil_t  gBrd(aMaxRow, aMaxCol, gameBoard, *ansiTerm);

Эта последняя строка вызвала ctor GOLUtil_t.

Экземпляр "gBrd" затем передается (по ссылке) в ctor игры, а оттуда - в любые агрегированные объекты, которые он содержит.

std::string retVal;
{
  // initialize display, initialize pattern
  GameOfLife_t  GOL(gBrd, timeOfDay, fillPatternChoiceLetter, useArray);

  std::string retValS = GOL.exec2(testMode);

  retVal = gBrd.clearGameBoard(retValS); // delete all cells
}
// force GameOfLife_t dtor before close ansiTerm

ansiTerm->close();

Резюме - нет глобальных.

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

Примечание: потому что один выходной терминал, я использовал один поток (основной)


Ваша первая попытка рефакторинга может быть:

а) удалить глобальные классы,

б) и вместо этого создайте их в main() (для управления временем жизни)

c) и затем передать по ссылке эти ранее глобальные экземпляры тем неглобальным объектам, которые их используют. Я рекомендую в ctor (s).

d) не забудьте убрать (удалите, если new'd)


моя среда: Ubuntu 15.10, 64-битная, g++ V5

Другие вопросы по тегам