Оператор Switch вместо нескольких вложенных if — else?

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

Это привело меня к целому ряду вложенных операторов if-else. Вот некоторый псевдокод для визуализации.

bool mainInit () {
if (!system1Init ()) {
reportError ();  // some error reporting function
}
else {
if (!system2Init ()) {
reportError ();
}
else {
if (!system3Init ()) {
// ... and so on

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

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

bool mainInit () {

switch (1) {
case 1:
if (!system1Init ()) {
reportError ();
break;
}
case 2:
if (!system2Init ())
reportError ();
break;
}
// ....
}

Теперь мне это нравится намного лучше. Мне гораздо легче читать, особенно с некоторыми приличными комментариями, но я довольно новичок в программировании.

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

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

Я провел поиск, но большая часть того, что я нашел, была связана с заменой цепочек операторов if — else if, а не заменой вложенных.

0

Решение

Ссылка на все системы в массиве, например std::vector<mySystem*>и зацикливайте их последовательно, прерывая при первом сбое. Таким образом, весь ваш код сокращается до менее чем 5 строк кода, даже для 500+ систем.

Предлагаемый взломать переключатель является злым примером Решение проблем XYВаша настоящая проблема в том, что у вас нет массива систем, и вы используете именованные переменные, что исключает все варианты более гибкого использования всех систем, как в цикле.

3

Другие решения

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

typedef (*system_init)(void);

system_init initialization_functions[] =
{
system1Init,
system2Init,
system3Init,
...
systemNInit
};

bool mainInit()
{
for(size_t idx(0); idx < sizeof(initialization_functions) / sizeof(initialization_functions[0]); ++idx)
{
if(!initialization_functions[idx]())
{
ReportError();
return false;
}
}
return true;
}

Тем не менее, ваш существующий код выглядит неправильно, так как первый mainInit() только звонки system1Init() и затем выходит. Наверное, не то, что вы хотели в первую очередь.

if(!system1Init())
{
ReportError();
return false;
}
// if you add an else, the system2Init() does not get called
// even if system1Init() succeeds
if(!system2Init())
{
ReportError();
return false;
}
[...]
return true;

Будет ли коммутатор ответить на вашу проблему? Не так, как было написано. То есть, если вы хотите позвонить mainInit() Функция со счетчиком, это может быть полезно. Drupal использует этот механизм:

bool mainInit(int idx)
{
bool r(true);
switch(idx)
{
case 1:
r = system1Init();
break;

case 2:
r = system2Init();
break;

[...]
}
if(!r)
{
ReportError();
}
return r
}

Обратите внимание, что механизм таблиц работает так же, как коммутатор. Пока весь код находится в systemNInit() функции (и это должно быть), коммутатор ничего не добавляет, так что вы можете сделать что-то вроде этого:

bool mainInit(int idx)
{
if(idx < 0 || idx >= sizeof(initialization_functions) / sizeof(initialization_functions[0]))
{
throw std::range_error("index out of bounds");
}
if(!initialization_functions[idx]())
{
ReportError();
return false;
}
return true;
}

Вызов mainInit() с индексом может быть полезным, если вы хотите правильно деинициализировать:

int main()
{
for(size_t idx(0); idx < ...; ++idx)
{
if(!mainInit(idx))
{
while(idx > 0)
{
--idx;
mainDeinit(idx);
}
exit(1);
}
}
...app do something here...
}
0

Используйте пользовательские исключения с четкими сообщениями об ошибках и добавьте код try-catch-report-die в коде main(), Существуют исключения для того, чтобы ваш случай выглядел хорошо, делая «плохой путь» неявным.

void initX() { ...; throw std::invalid_argument_exception("..."); }

int main() {
try {
init1(); init2(); ... run();
return 0;
} catch (std::exception const& e) {
log(e.what()); exit 42;
}
}
0

Я бы сделал это так:

bool mainInit () {
if (!system1Init ()) {
return(false);
}

if (!system2Init ()) {
return(false);
}

if (!system3Init ()) {
return(false);
}

//...

return(true);
}

//...

if(!mainInit()) {
reportError();
}
0
По вопросам рекламы [email protected]