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