Почему main () должно быть коротким?

87

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

Сначала я понятия не имел, почему. Я просто повиновался, не понимая, к радости моих профессоров.

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

Перемотав несколько недель назад, я посмотрел на исходный код Python и нашел main()функцию:

/* Minimal main program -- everything is loaded from the library */

...

int
main(int argc, char **argv)
{
    ...
    return Py_Main(argc, argv);
}

Yay питон. Короткая main()функция == Хороший код.

Учителя программирования были правы.

Желая заглянуть глубже, я взглянул на Py_Main. В целом он определяется следующим образом:

/* Main program */

int
Py_Main(int argc, char **argv)
{
    int c;
    int sts;
    char *command = NULL;
    char *filename = NULL;
    char *module = NULL;
    FILE *fp = stdin;
    char *p;
    int unbuffered = 0;
    int skipfirstline = 0;
    int stdin_is_interactive = 0;
    int help = 0;
    int version = 0;
    int saw_unbuffered_flag = 0;
    PyCompilerFlags cf;

    cf.cf_flags = 0;

    orig_argc = argc;           /* For Py_GetArgcArgv() */
    orig_argv = argv;

#ifdef RISCOS
    Py_RISCOSWimpFlag = 0;
#endif

    PySys_ResetWarnOptions();

    while ((c = _PyOS_GetOpt(argc, argv, PROGRAM_OPTS)) != EOF) {
        if (c == 'c') {
            /* -c is the last option; following arguments
               that look like options are left for the
               command to interpret. */
            command = (char *)malloc(strlen(_PyOS_optarg) + 2);
            if (command == NULL)
                Py_FatalError(
                   "not enough memory to copy -c argument");
            strcpy(command, _PyOS_optarg);
            strcat(command, "\n");
            break;
        }

        if (c == 'm') {
            /* -m is the last option; following arguments
               that look like options are left for the
               module to interpret. */
            module = (char *)malloc(strlen(_PyOS_optarg) + 2);
            if (module == NULL)
                Py_FatalError(
                   "not enough memory to copy -m argument");
            strcpy(module, _PyOS_optarg);
            break;
        }

        switch (c) {
        case 'b':
            Py_BytesWarningFlag++;
            break;

        case 'd':
            Py_DebugFlag++;
            break;

        case '3':
            Py_Py3kWarningFlag++;
            if (!Py_DivisionWarningFlag)
                Py_DivisionWarningFlag = 1;
            break;

        case 'Q':
            if (strcmp(_PyOS_optarg, "old") == 0) {
                Py_DivisionWarningFlag = 0;
                break;
            }
            if (strcmp(_PyOS_optarg, "warn") == 0) {
                Py_DivisionWarningFlag = 1;
                break;
            }
            if (strcmp(_PyOS_optarg, "warnall") == 0) {
                Py_DivisionWarningFlag = 2;
                break;
            }
            if (strcmp(_PyOS_optarg, "new") == 0) {
                /* This only affects __main__ */
                cf.cf_flags |= CO_FUTURE_DIVISION;
                /* And this tells the eval loop to treat
                   BINARY_DIVIDE as BINARY_TRUE_DIVIDE */
                _Py_QnewFlag = 1;
                break;
            }
            fprintf(stderr,
                "-Q option should be `-Qold', "
                "`-Qwarn', `-Qwarnall', or `-Qnew' only\n");
            return usage(2, argv[0]);
            /* NOTREACHED */

        case 'i':
            Py_InspectFlag++;
            Py_InteractiveFlag++;
            break;

        /* case 'J': reserved for Jython */

        case 'O':
            Py_OptimizeFlag++;
            break;

        case 'B':
            Py_DontWriteBytecodeFlag++;
            break;

        case 's':
            Py_NoUserSiteDirectory++;
            break;

        case 'S':
            Py_NoSiteFlag++;
            break;

        case 'E':
            Py_IgnoreEnvironmentFlag++;
            break;

        case 't':
            Py_TabcheckFlag++;
            break;

        case 'u':
            unbuffered++;
            saw_unbuffered_flag = 1;
            break;

        case 'v':
            Py_VerboseFlag++;
            break;

#ifdef RISCOS
        case 'w':
            Py_RISCOSWimpFlag = 1;
            break;
#endif

        case 'x':
            skipfirstline = 1;
            break;

        /* case 'X': reserved for implementation-specific arguments */

        case 'U':
            Py_UnicodeFlag++;
            break;
        case 'h':
        case '?':
            help++;
            break;
        case 'V':
            version++;
            break;

        case 'W':
            PySys_AddWarnOption(_PyOS_optarg);
            break;

        /* This space reserved for other options */

        default:
            return usage(2, argv[0]);
            /*NOTREACHED*/

        }
    }

    if (help)
        return usage(0, argv[0]);

    if (version) {
        fprintf(stderr, "Python %s\n", PY_VERSION);
        return 0;
    }

    if (Py_Py3kWarningFlag && !Py_TabcheckFlag)
        /* -3 implies -t (but not -tt) */
        Py_TabcheckFlag = 1;

    if (!Py_InspectFlag &&
        (p = Py_GETENV("PYTHONINSPECT")) && *p != '\0')
        Py_InspectFlag = 1;
    if (!saw_unbuffered_flag &&
        (p = Py_GETENV("PYTHONUNBUFFERED")) && *p != '\0')
        unbuffered = 1;

    if (!Py_NoUserSiteDirectory &&
        (p = Py_GETENV("PYTHONNOUSERSITE")) && *p != '\0')
        Py_NoUserSiteDirectory = 1;

    if ((p = Py_GETENV("PYTHONWARNINGS")) && *p != '\0') {
        char *buf, *warning;

        buf = (char *)malloc(strlen(p) + 1);
        if (buf == NULL)
            Py_FatalError(
               "not enough memory to copy PYTHONWARNINGS");
        strcpy(buf, p);
        for (warning = strtok(buf, ",");
             warning != NULL;
             warning = strtok(NULL, ","))
            PySys_AddWarnOption(warning);
        free(buf);
    }

    if (command == NULL && module == NULL && _PyOS_optind < argc &&
        strcmp(argv[_PyOS_optind], "-") != 0)
    {
#ifdef __VMS
        filename = decc$translate_vms(argv[_PyOS_optind]);
        if (filename == (char *)0 || filename == (char *)-1)
            filename = argv[_PyOS_optind];

#else
        filename = argv[_PyOS_optind];
#endif
    }

    stdin_is_interactive = Py_FdIsInteractive(stdin, (char *)0);

    if (unbuffered) {
#if defined(MS_WINDOWS) || defined(__CYGWIN__)
        _setmode(fileno(stdin), O_BINARY);
        _setmode(fileno(stdout), O_BINARY);
#endif
#ifdef HAVE_SETVBUF
        setvbuf(stdin,  (char *)NULL, _IONBF, BUFSIZ);
        setvbuf(stdout, (char *)NULL, _IONBF, BUFSIZ);
        setvbuf(stderr, (char *)NULL, _IONBF, BUFSIZ);
#else /* !HAVE_SETVBUF */
        setbuf(stdin,  (char *)NULL);
        setbuf(stdout, (char *)NULL);
        setbuf(stderr, (char *)NULL);
#endif /* !HAVE_SETVBUF */
    }
    else if (Py_InteractiveFlag) {
#ifdef MS_WINDOWS
        /* Doesn't have to have line-buffered -- use unbuffered */
        /* Any set[v]buf(stdin, ...) screws up Tkinter :-( */
        setvbuf(stdout, (char *)NULL, _IONBF, BUFSIZ);
#else /* !MS_WINDOWS */
#ifdef HAVE_SETVBUF
        setvbuf(stdin,  (char *)NULL, _IOLBF, BUFSIZ);
        setvbuf(stdout, (char *)NULL, _IOLBF, BUFSIZ);
#endif /* HAVE_SETVBUF */
#endif /* !MS_WINDOWS */
        /* Leave stderr alone - it should be unbuffered anyway. */
    }
#ifdef __VMS
    else {
        setvbuf (stdout, (char *)NULL, _IOLBF, BUFSIZ);
    }
#endif /* __VMS */

#ifdef __APPLE__
    /* On MacOS X, when the Python interpreter is embedded in an
       application bundle, it gets executed by a bootstrapping script
       that does os.execve() with an argv[0] that's different from the
       actual Python executable. This is needed to keep the Finder happy,
       or rather, to work around Apple's overly strict requirements of
       the process name. However, we still need a usable sys.executable,
       so the actual executable path is passed in an environment variable.
       See Lib/plat-mac/bundlebuiler.py for details about the bootstrap
       script. */
    if ((p = Py_GETENV("PYTHONEXECUTABLE")) && *p != '\0')
        Py_SetProgramName(p);
    else
        Py_SetProgramName(argv[0]);
#else
    Py_SetProgramName(argv[0]);
#endif
    Py_Initialize();

    if (Py_VerboseFlag ||
        (command == NULL && filename == NULL && module == NULL && stdin_is_interactive)) {
        fprintf(stderr, "Python %s on %s\n",
            Py_GetVersion(), Py_GetPlatform());
        if (!Py_NoSiteFlag)
            fprintf(stderr, "%s\n", COPYRIGHT);
    }

    if (command != NULL) {
        /* Backup _PyOS_optind and force sys.argv[0] = '-c' */
        _PyOS_optind--;
        argv[_PyOS_optind] = "-c";
    }

    if (module != NULL) {
        /* Backup _PyOS_optind and force sys.argv[0] = '-c'
           so that PySys_SetArgv correctly sets sys.path[0] to ''
           rather than looking for a file called "-m". See
           tracker issue #8202 for details. */
        _PyOS_optind--;
        argv[_PyOS_optind] = "-c";
    }

    PySys_SetArgv(argc-_PyOS_optind, argv+_PyOS_optind);

    if ((Py_InspectFlag || (command == NULL && filename == NULL && module == NULL)) &&
        isatty(fileno(stdin))) {
        PyObject *v;
        v = PyImport_ImportModule("readline");
        if (v == NULL)
            PyErr_Clear();
        else
            Py_DECREF(v);
    }

    if (command) {
        sts = PyRun_SimpleStringFlags(command, &cf) != 0;
        free(command);
    } else if (module) {
        sts = RunModule(module, 1);
        free(module);
    }
    else {

        if (filename == NULL && stdin_is_interactive) {
            Py_InspectFlag = 0; /* do exit on SystemExit */
            RunStartupFile(&cf);
        }
        /* XXX */

        sts = -1;               /* keep track of whether we've already run __main__ */

        if (filename != NULL) {
            sts = RunMainFromImporter(filename);
        }

        if (sts==-1 && filename!=NULL) {
            if ((fp = fopen(filename, "r")) == NULL) {
                fprintf(stderr, "%s: can't open file '%s': [Errno %d] %s\n",
                    argv[0], filename, errno, strerror(errno));

                return 2;
            }
            else if (skipfirstline) {
                int ch;
                /* Push back first newline so line numbers
                   remain the same */
                while ((ch = getc(fp)) != EOF) {
                    if (ch == '\n') {
                        (void)ungetc(ch, fp);
                        break;
                    }
                }
            }
            {
                /* XXX: does this work on Win/Win64? (see posix_fstat) */
                struct stat sb;
                if (fstat(fileno(fp), &sb) == 0 &&
                    S_ISDIR(sb.st_mode)) {
                    fprintf(stderr, "%s: '%s' is a directory, cannot continue\n", argv[0], filename);
                    fclose(fp);
                    return 1;
                }
            }
        }

        if (sts==-1) {
            /* call pending calls like signal handlers (SIGINT) */
            if (Py_MakePendingCalls() == -1) {
                PyErr_Print();
                sts = 1;
            } else {
                sts = PyRun_AnyFileExFlags(
                    fp,
                    filename == NULL ? "<stdin>" : filename,
                    filename != NULL, &cf) != 0;
            }
        }

    }

    /* Check this environment variable at the end, to give programs the
     * opportunity to set it from Python.
     */
    if (!Py_InspectFlag &&
        (p = Py_GETENV("PYTHONINSPECT")) && *p != '\0')
    {
        Py_InspectFlag = 1;
    }

    if (Py_InspectFlag && stdin_is_interactive &&
        (filename != NULL || command != NULL || module != NULL)) {
        Py_InspectFlag = 0;
        /* XXX */
        sts = PyRun_AnyFileFlags(stdin, "<stdin>", &cf) != 0;
    }

    Py_Finalize();
#ifdef RISCOS
    if (Py_RISCOSWimpFlag)
        fprintf(stderr, "\x0cq\x0c"); /* make frontend quit */
#endif

#ifdef __INSURE__
    /* Insure++ is a memory analysis tool that aids in discovering
     * memory leaks and other memory problems.  On Python exit, the
     * interned string dictionary is flagged as being in use at exit
     * (which it is).  Under normal circumstances, this is fine because
     * the memory will be automatically reclaimed by the system.  Under
     * memory debugging, it's a huge source of useless noise, so we
     * trade off slower shutdown for less distraction in the memory
     * reports.  -baw
     */
    _Py_ReleaseInternedStrings();
#endif /* __INSURE__ */

    return sts;
}

Боже, Всемогущий ... он достаточно большой, чтобы потопить Титаник.

Кажется, что Python проделал трюк «Введение в программирование 101» и просто переместил весь main()код в другую функцию, названную чем-то очень похожим на «main».

Вот мой вопрос: этот код написан ужасно, или есть другие причины иметь короткую основную функцию?

В нынешнем виде я не вижу абсолютно никакой разницы между этим и простым перемещением кода Py_Main()обратно main(). Я ошибаюсь, думая об этом?

riwalk
источник
4
Разве это не будет лучше для codereviews.stackexchange.com ?
Foobar
38
@ Лужин, нет. Я не прошу никого пересматривать исходный код Python. Это вопрос программирования.
riwalk
3
TBH, половина кода - это обработка опций, и каждый раз, когда ваша программа поддерживает множество опций, и вы пишете собственный процессор, это то, что вы в конечном итоге делаете ...
Nim
7
@Star No, Programmers.SE также за лучшие практики, стили кодирования и т. Д. Собственно, для этого я и посещаю сайт.
Матин Улхак,
4
@Nim, я понимаю , что это то , что он делает, но нет никаких причин , чтобы не писать , как , options = ParseOptionFlags(argc,argv)где optionsэто , structчто содержит переменные Py_BytesWarningFlag, Py_DebugFlagи т.д ...
riwalk

Ответы:

137

Вы не можете экспортировать mainиз библиотеки, но вы можете экспортировать Py_Main, и тогда любой, кто использует эту библиотеку, может многократно «вызывать» Python с разными аргументами в одной и той же программе. В этот момент pythonстановится просто еще одним потребителем библиотеки, чуть больше, чем обертка для функции библиотеки; он звонит так Py_Mainже, как и все остальные.

Роб Кеннеди
источник
1
Там хороший ответ.
riwalk
26
Полагаю, было бы точнее сказать, что вы не можете импортировать его, @Shoosh. Стандарт C ++ запрещает вызывать его из вашего собственного кода. Кроме того, его связь определяется реализацией. Кроме того, возвращаясь к mainэффективным вызовам exit, чего обычно не требуется делать библиотеке.
Роб Кеннеди
3
@Coder, см. C ++ 03 §3.6.1 / 5: «Оператор return in mainоставляет эффект основной функции… и вызывает его exitс возвращаемым значением в качестве аргумента». Также см. §18.3 / 8, в котором объясняется, что «объекты со статической продолжительностью хранения уничтожаются» и «все открытые потоки C… сбрасываются» при вызове exit. С99 имеет похожий язык.
Роб Кеннеди
1
@ Кодер, exitлистья mainне имеют значения. Мы не обсуждаем поведение exit. Мы обсуждаем поведение main. И поведение main включает в себя поведение exit, что бы это ни было. Вот что делает нежелательным импорт и вызов main(если такое вообще возможно или разрешено).
Роб Кеннеди
3
@Coder, если возврат из mainне вызывает обращения exitк вашему компилятору, то ваш компилятор не следует стандарту. То, что стандарт диктует такое поведение, mainдоказывает, что в этом есть что-то особенное. Особенность в том, mainчто возвращение из него имеет эффект звонка exit. ( Как это происходит, зависит от авторов компилятора. Компилятор может просто вставить код в эпилог функции, который уничтожает статические объекты, вызывает atexitпроцедуры, сбрасывает файлы и завершает программу - что, опять же, не то, что вам нужно в библиотеке .)
Роб Кеннеди
42

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

В вашем примере нет никакой выгоды от перемещения кода из main.

Марк Б
источник
9
Золотое слово может быть «повторное использование». Длинный mainне очень многоразовый.
S.Lott
1
@S - Это одно золотое слово. Еще один OMG !!! СДВГ ПРОСТО ПИЛЕТ !!!! или с точки зрения непрофессионала: разборчивость.
Эдвард Стрендж,
3
main () также имеет некоторые ограничения, которых нет у других функций.
Мартин Йорк,
1
Также main () не имеет реального значения. Ваш код должен что-то значить для другого программиста. Я использую main для разбора аргументов и все - и я даже делегирую это, если это больше, чем несколько строк.
Билл К
@ Bill K: Хорошая мысль: использование main () только для разбора аргументов (и запуска остальной части программы) также соответствует принципу единой ответственности.
Джорджио
28

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

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

Примечание: идея пришла отсюда .

шанс
источник
Еще один хороший. Никогда не думал об этом аспекте.
riwalk
16

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

В конкретном случае, о котором вы упомянули выше, mainон короткий, потому что вся эта сложность учтена Py_Main; если вы хотите, чтобы ваш код вел себя как оболочка Python, вы можете просто использовать этот код без особых хлопот. (Это должно быть учтено так, потому что это не работает, если вы кладете mainв библиотеку; странные вещи случаются, если вы делаете.)

РЕДАКТИРОВАТЬ:
Чтобы уточнить, mainне может быть в статической библиотеке, потому что она не имеет явной ссылки на нее и поэтому не будет связана правильно (если вы не поместите его в объектный файл с чем-то, на что ссылаются, что просто ужасно !) Общие библиотеки обычно рассматриваются как похожие (опять же, чтобы избежать путаницы), хотя на многих платформах дополнительным фактором является то, что общая библиотека - это просто исполняемый файл без раздела начальной загрузки (из которых mainтолько последняя и наиболее видимая часть ).

Donal Fellows
источник
1
Короче говоря, не кладите mainв библиотеку. Это либо не сработает, либо ужасно вас запутает. Но делегировать практически всю свою работу функции, которая находится в lib, часто разумно.
Донал Феллоуз
6

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

И да, ваш пример ужасен и труден для чтения, не говоря уже о поддержке.

Эд С.
источник
Да, я всегда подозревал, что сам код ужасен (хотя вопрос касался размещения кода, а не самого кода). Я боюсь, что мое видение Python было повреждено в результате ...
riwalk
1
@stargazer: я не знаю, что сам код ужасен, просто он плохо организован для потребления человеком. Тем не менее, существует множество «уродливых» кодов, которые хорошо работают и работают великолепно. Красота кода - это еще не все, хотя мы всегда должны стараться писать максимально чистый код.
Эд С.
Мех. Для меня они одно и то же. Чистый код имеет тенденцию быть более стабильным.
riwalk
Код не страшен, в основном это случаи переключения и обработки нескольких платформ. Что именно ты находишь ужасным?
Франческо
@Francesco: Извините, «Ужасный» с точки зрения обслуживания и читабельности, а не функциональный.
Эд С.
1

Некоторым людям нравится более 50 функций, которые больше ничего не делают, но заключают вызов в другую функцию. Я бы предпочел нормальную основную функцию, которая выполняет основную логику программы. Хорошо структурировано, конечно.

int main()
{
CheckInstanceCountAndRegister();
InitGlobals();
ProcessCmdParams();
DoInitialization();
ProgramMainLoopOrSomething();
DeInit();
ClearGlobals();
UnregisterInstance();
return 0; //ToMainCRTStartup which cleans heap, etc.
}

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

Это чисто личный вкус.

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

Лучше всего, чтобы ВСЕ ваши функции были короткими, а не только основными. Однако «короткий» субъективен, он зависит от размера вашей программы и языка, который вы используете.

Майк Миллер
источник
0

Там нет требования mainбыть какой-либо длины, кроме стандартов кодирования. mainэто функция, как и любая другая, и, как таковая, ее сложность должна быть ниже 10 (или что бы там ни говорили ваши стандарты кодирования). Вот и все, все остальное скорее аргументировано.

редактировать

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

Что касается конкретного кода в вашем вопросе - да, это безобразно.

Что касается вашего второго вопроса - да, вы не правы . Перемещение всего этого кода обратно в main не позволяет вам использовать его как библиотеку, связываясь Py_Mainизвне.

Теперь я ясно?

littleadv
источник
Я не спрашивал, может ли это быть долго. Я спросил, почему это не должно быть долго.
riwalk
«Сложность ниже 10»? Есть ли единица измерения для этого?
Донал Феллоуз
@ Stargazer712 Длина функции обычно также регулируется стандартами кодирования. Это проблема читабельности (и сложность, обычно длинные функции разветвлены, так что сложность намного выше 20), и, как я уже сказал, mainничем не отличается от любой другой функции в этом отношении.
littleadv
@Donal - да, нажмите на ссылку.
littleadv
Я собираюсь понизить этот бутон. Вы полностью упускаете смысл вопроса.
riwalk
0

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

В большинстве целей с поддержкой именованных разделов функции, используемые только при запуске (статические конструкторы и main ), функции, используемые только при выходе, и функции, обнаруженные как «холодные», помещаются в отдельные подразделы текстового сегмента . Это расширяет функцию -freorder-functions и управляется тем же ключом. Цель состоит в том, чтобы улучшить время запуска больших программ на C ++.

Подсветка добавлена ​​мной.

Петр Григорьевич
источник
0

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

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

Но кодовая база Python выдвигает код не для Py_Mainигры в это правило, а потому, что вы не можете экспортировать mainиз библиотеки или вызывать ее как функцию.

Джек Эйдли
источник
-1

Есть несколько технических ответов выше, давайте оставим это в стороне.

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

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

Бетонная олуша
источник
Вы предполагаете, что «код C ++ должен использовать объекты и только объекты». Это неправда, C ++ - это мультипарадигмальный язык, который не навязывает все в ООП, как некоторые другие языки.
Бен Фойгт