Что входит в основную функцию?

17

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

1: Напишите класс «Мастер», который получает параметры, переданные основной функции, и обрабатывает полную программу в этом классе «Мастер» (конечно, вы также используете другие классы). Поэтому основная функция будет сводиться к минимуму строк.

#include "MasterClass.h"
int main(int args, char* argv[])
{
MasterClass MC(args, argv);
}

2: Напишите «полную» программу в основной функции, используя, конечно же, определенные пользователем объекты! Однако есть и глобальные функции, и основная функция может быть несколько большой.

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

    
задан Woltan 12.01.2011 в 12:23
источник

10 ответов

24

Почему у вас есть один мастер-класс? Какова его область ответственности single ?

Классы «Мастер» или «приложение» обычно становятся крупными блобами, которые делают слишком много разных вещей. В конечном счете, в чем смысл? Что вы купили?

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

Включение такого сверхвысокого потока в main означает, что его легко найти, потому что main - это ваша отправная точка в любом случае. Здесь вы начнете искать, если хотите понять код. Поэтому поставьте в нем то, что читатель хочет знать, пытаясь понять код.

Конечно, вы могли бы взять все это и поместить в «основной класс», и вы бы не получили абсолютно ничего, кроме удовлетворения всех Java-луддитов, которые чувствуют, что «все должно быть в классе», .     

ответ дан jalf 12.01.2011 в 12:48
источник
6

Вы описываете два «экстремальных» подхода, ни один из которых не подходит мне. Ни один из God Class , ни наличие единственной функции Бога - это правильный способ реализации любой нетривиальной программы, предназначенной для реального использования .

В вашем MasterClass может быть один вызов main() (хотя я бы предпочел лучше использовать функции разбиения на разделы, например, выполнить любую обработку в командной строке в main() , чтобы отделить MasterClass от детали параметров командной строки). Однако, если этому классу сложно провести единичный тест, это признак проблемы проектирования, и обычно решение заключается в деле делеции части или всей проверяемой функциональности другим классам, где ее можно легко протестировать в отдельности через общие интерфейсы .

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

Сладкое место, где вы хотите быть, находится где-то между двумя крайностями, сдерживается требованием сделать ваш код пригодным для тестирования.

Стоит подумать о том, как структурировать ваши программы в целом, а не только в контексте main() . Основная идея состоит в том, чтобы разбить его на «куски» (классы и методы), которые

  • достаточно маленький, чтобы его можно было легко понимать, проверять и поддерживать,
  • логически связный.
ответ дан Péter Török 12.01.2011 в 12:28
источник
3

Я бы сделал анализ аргументов процесса в основной подпрограмме, а затем создал экземпляр класса, передав более читаемые параметры, чем argc и argv.

    
ответ дан Benoit Thiery 12.01.2011 в 12:27
источник
2

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

    
ответ дан trojanfoe 12.01.2011 в 12:25
источник
2

Сначала я редко использую C ++, но я предполагаю, что это не проблема языка.
Ну, я думаю, это сводится к тому, чтобы попробовать в стороне от некоторых вопросов практичности. Я лично предпочитаю использовать макет # 1, но не ставьте подпрограммы командной строки в MasterClass . Для меня это явно принадлежит к основному. MasterClass должен получить аргументы parsed (целые числа, FileStreams, что угодно).

    
ответ дан Surma 12.01.2011 в 12:29
источник
2

Обычно я называю главную функцию (с той же сигнатурой) в пространстве имен приложения:

namespace current_application {
    int main( int argc, char **argv ) {
        // ...
    }
}
int main( int argc, char **argv ) {
    return current_application::main( argc, argv );
}

И затем я обычно использую свой фактический основной (тот, что в пространстве имен), чтобы инициализировать приложение wise :

  • установить локали на стандартный ввод / вывод / ошибку)

  • параметры параметров анализа

  • создать экземпляр объекта моего основного класса, если он присутствует (эквивалент чего-то вроде QApplication )

  • вызвать основную функцию, если она присутствует (эквивалент чего-то вроде QApplication::run )

и обычно предпочитают добавлять блок try catch , чтобы распечатать дополнительную информацию об отладке в случае сбоя.

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

    
ответ дан peoro 12.01.2011 в 12:37
источник
0

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

    
ответ дан Puppy 12.01.2011 в 12:29
источник
0

Если исключение не имеет обработчика, то не определено, вызываются ли деструкторы локальных объектов до std::terminate .

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

Обычно это все, что я вложил в main , который иначе просто вызывает cppMain ...; -)

Cheers & amp; НТН.

    
ответ дан Cheers and hth. - Alf 12.01.2011 в 12:57
источник
0

Я предпочитаю подход IOC (Inversion of Control) к моим программам.

Поэтому мой «основной» использует аргументы команды для определения «параметров» и «конфигурации» программы. По вариантам я подразумеваю интерпретацию определенных флагов, передаваемых в командной строке, и по конфигурации я имею в виду загрузку файлов конфигурации.

Объект, который он использует для загрузки файлов конфигурации, затем имеет команду «запустить», но то, что запускается (если есть), зависит также от аргументов командной строки.

    
ответ дан CashCow 12.01.2011 в 13:06
источник
0

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

class MasterClass {
public:
static MasterClass* CreateInstance( int argc, char **argv );
    // ...
}

int main(int argc, char** argv)
{
    try
    {
         MasterClass mc = MC::CreateInstance(argc, argv);
    }
    catch(...)
    {
        // ...
    }
}

Это также имеет то преимущество, что любая обработка, которая не имеет ничего общего с реальной программной логикой, такой как чтение среды и т. д., не обязательно должна быть помещена в MasterClass. Они могут идти в main (). Хорошим примером является задача надстройки сервера для системы Lotus Domino. Здесь задача должна выполняться только тогда, когда управление передается ему заданием планировщика Domino. Здесь главное будет выглядеть, как показано ниже:

STATUS LNPUBLIC AddInMain(HMODULE hModule, int argc, char far *argv[])
{
     MasterClass mc = MC::CreateInstance(argc, argv);
     while(/* wait for scheduler to give control */)
     {
          STATUS s = mc->RunForTimeSlice();
          if (s != SUCCESS)
               break;
     }
     // clean up
}

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

    
ответ дан 341008 12.01.2011 в 12:52
источник