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

18

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

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

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

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

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

    
задан Woltan 12.01.2011 в 12:23
источник
  • Обычно argc записывается вместо args. argc - количество аргументов –  Will 04.03.2017 в 11:27

10 ответов

23

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

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

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

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

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

ответ дан jalf 12.01.2011 в 12:48
источник
  • +1, если только для разбора аргументов. Если вы хотите написать «модульный» код, необходимо отделить разбор аргументов (один интерфейс среди других) от фактического кода и использовать соответствующую структуру для сбора всех этих «настроек». Обратите внимание, что синтаксический анализ аргументов также может включать использование переменных среды, поскольку в большинстве случаев они используются для настройки по умолчанию. Затем это тривиальная настройка / петля до тех пор, пока не будет выполнено / очищено высокоуровневое представление. –  Matthieu M. 12.01.2011 в 13:35
  • «В конечном счете, в чем смысл?» Хотя это не показано здесь, оно может обеспечить инъекцию зависимости. Вместо того, чтобы, например, писать код в std :: cout, Мастер-класс связывает с потоком standardOutput, std :: ostream. Функция main () создает объект-мастер с std :: cout для этой ассоциации, но модульные тесты могут использовать std :: ostringstream. После этого становится легче выполнять требования по тестированию на уровне, такие как «если ошибка в командной строке, программа должна записать сообщение об ошибке в свой стандартный выходной поток». –  Raedwald 12.01.2011 в 14:09
  • @ Raedwald: для чего вам нужен мастер-класс? Почему главное не может создать соответствующий std :: ostream, а затем передать его всем классам и функциям, которые он делегирует? Для этого не нужен мастер-класс. –  jalf 12.01.2011 в 14:12
  • Но вы попали в действительную точку, подчеркнув это одно предложение. «В чем смысл» - это реальный вопрос, а не просто риторический «нет смысла». Это то, о чем вы должны спросить себя, потому что в некоторых случаях может быть смысл создать мастер-класс. Я просто так часто говорю, что это не что иное, как упражнение в Java-мысли, и создание ненужных слоев косвенности. И если вы не можете придумать четкий ответ на «какой смысл» в вашем случае, тогда вам не следует создавать класс «хозяин». –  jalf 12.01.2011 в 14:14
  • имеет ли смысл «единичный тест» на основную функцию? если что-нибудь я увижу, это скорее функциональное тестирование или что-то еще. –  João Portela 12.01.2011 в 15:45
Показать остальные комментарии
6

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

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

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

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

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

  • достаточно маленький, чтобы его можно было легко понимать, проверять и поддерживать,
  • логически связный.
ответ дан Péter Török 12.01.2011 в 12:28
источник
  • Я не согласен. Должна быть точка входа - будь то функция Бога или класс Бога, должен быть хотя бы один. –  Puppy 12.01.2011 в 12:29
  • @DeadMG, конечно. Но цитирование OP: «написать некоторый единичный тест для первого подхода [...] немного сложно, поскольку большинство методов являются частными». Это я интерпретирую так, что его Мастер-класс - это класс Бога, а не делегирование чего-либо нигде. –  Péter Török 12.01.2011 в 12:33
  • О, я понимаю, что вы имеете в виду. Да, я полностью согласен. –  Puppy 12.01.2011 в 12:34
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
источник
  • Я не вижу, что это заставляет вас. Кроме запутанных программистов. :) –  John Dibling 12.01.2011 в 13:01
  • @ Джун Диблинг: почему? –  peoro 12.01.2011 в 14:48
  • Потому что вы пытаетесь сделать что-то Java-ish на C ++. Такая архитектура, где вы просто делят main () на свободную функцию в пространстве имен, ничего не дает вам и неожиданна. Что делает основной объект namespaced для вас тем, что нету имени, которого нет, нет? –  John Dibling 12.01.2011 в 18:21
  • @John Dibling: моя основная функция приложения логически принадлежит пространству имен приложений, поэтому я думаю, что это чище, чтобы поместить его туда. Более того, помещая main в мое пространство имен, я могу использовать символы, определенные в пространстве имен, без необходимости их явного извлечения (или иметь директиву using). Наконец, это добавляет еще один уровень абстракции: у меня есть функция вне моей основной функции, она часто бывает полезной для быстрых хаков / тестов, и это легко позволит мне использовать мое приложение в качестве библиотеки: если мне нужно использовать большую часть код для других проектов, просто получить все это и переписать :: main (это популярно в Python) –  peoro 12.01.2011 в 18:50
1

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

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

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

Cheers & amp; НТН.

    
ответ дан Cheers and hth. - Alf 12.01.2011 в 12:57
источник
  • Другим трюком является то, что вместо объектов в глобальной области действия вы можете иметь указатели в глобальной области видимости, а затем строить объекты внутри основного (и, следовательно, контролировать порядок построения), и указывать указатели на эти локали, а затем вызывать «реальная» основная функция. Таким образом, детерминированное построение и уничтожение и упорядочение этого :) –  Karl Knechtel 12.01.2011 в 13:41
0

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

    
ответ дан Puppy 12.01.2011 в 12:29
источник
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
источник