Чому 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);
}

Так, пітон. Коротка 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()код Росії на іншу функцію, назвавши його чимось дуже схожим на "основний".

Ось моє запитання: Чи страшенно написаний цей код, чи є інші причини для короткої основної функції?

Як зараз, я не бачу абсолютно ніякої різниці між тим, як зробити це, і просто перемістити код Py_Main()назад в main(). Я помиляюся, думаючи про це?


4
Хіба це не буде краще для codereviews.stackexchange.com ?
foobar

38
@Лужин, ні. Я не прошу нікого переглядати вихідний код Python. Це питання програмування.
riwalk

3
TBH, половина коду - це обробка опцій, і в будь-який час ваша програма підтримує безліч варіантів, і ви пишете власний процесор, це те, що ви в кінцевому підсумку робите ...
Нім

7
@Star Ні, Programmers.SE також для кращих практик, стилів кодування тощо. Насправді саме це я відвідую на сайті.
Mateen Ulhaq

4
@Nim, я розумію, що це робить, але немає причин не писати це як options = ParseOptionFlags(argc,argv)де optionsзнаходиться a, structщо містить змінні Py_BytesWarningFlag, Py_DebugFlagі т.
Д.

Відповіді:


137

Ви не можете експортувати mainз бібліотеки, але ви можете експортувати Py_Main, і тоді кожен, хто використовує цю бібліотеку, може "викликати" Python багато разів різними аргументами в одній програмі. У цей момент pythonстає просто ще одним споживачем бібліотеки, трохи більше, ніж обгорткою для бібліотечної функції; він дзвонить так Py_Mainсамо, як і всі інші.


1
Є хороша відповідь.
riwalk

26
Я думаю, може бути більш точним, сказати, що ви не можете імпортувати його, @Shoosh. Стандарт C ++ забороняє називати його з власного коду. Крім того, її зв'язок визначається реалізацією. Крім того, повертаючись з mainефективних дзвінків exit, які зазвичай не хочуть робити бібліотеки.
Роб Кеннеді

3
@Coder, див. C ++ 03 §3.6.1 / 5: "Оператор зворотного зв’язку в результаті призводить mainдо залишення основної функції ... та виклику exitзі значенням повернення як аргумент." Також дивіться §18.3 / 8, де пояснюється, що "об'єкти зі статичною тривалістю зберігання знищені" та "всі відкриті потоки C ... змиваються" під час дзвінка exit. C99 має схожу мову.
Роб Кеннеді

1
@Coder, чи не має значення 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 !!! ADHD ПЕРЕКРИЛУВАТИ !!!! або по-простому: розбірливість.
Edward Strange

3
main () також має деякі обмеження, яких інші функції не мають.
Мартін Йорк

1
Також main () не має реального значення. Ваш код повинен щось означати для іншого програміста. Я використовую main для розбору аргументів, і це все - і я навіть делегую це, якщо це більше кількох рядків.
Білл К

@Bill K: Добре, використання main () лише для розбору аргументів (та запуску решти програми) також відповідає принципу єдиної відповідальності.
Джорджіо

28

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

Написання модульованого коду та дотримання принципу єдиної відповідальності дозволили моєму коду розробитись у "пучках", а main () слугував не що інше, як каталізатор для запуску програми.

Примітка. Ідею я взяв звідси .


Ще один хороший. Ніколи не думав про цей аспект.
riwalk

16

Це рідко гарна ідея mainбути довгим; як з будь-якою функцією (або методом), якщо це довго, ви, мабуть, не вистачаєте можливостей для рефакторингу.

У конкретному випадку, який ви згадуєте вище, mainвін короткий, оскільки вся ця складність з'ясовується Py_Main; якщо ви хочете, щоб ваш код поводився як оболонка python, ви можете просто використовувати цей код, не маючи багато чого. (Це потрібно враховувати таким чином, тому що воно не працює добре, якщо ви поміщаєте mainв бібліотеку; якщо у вас трапляються випадкові речі.)

EDIT:
Для уточнення, mainне можна знаходитись у статичній бібліотеці, оскільки вона не має явного посилання на неї і тому не буде пов’язана належним чином (якщо ви не виділите її в об'єктний файл із тим, на що йдеться, що просто жахливо !) Спільні бібліотеки зазвичай трактуються як подібні (знову ж таки, для запобігання плутанини), хоча на багатьох платформах додатковим фактором є те, що спільна бібліотека - це просто виконуваний файл без розділу завантаження (з яких mainє лише останньою і найбільш видимою частиною ).


1
Якщо коротко, не покладіть mainу бібліотеку. Це або не спрацює, або збентежить вас страшенно. Але делегувати практично всю свою роботу функції, яка знаходиться в лібі, це часто розумно.
Стипендіати доналу

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не повинна бути короткою. Або довго. Він повинен включати функціонал, який потрібно виконувати на основі вашого дизайну, та дотримуватися стандартів кодування.

Щодо конкретного коду у вашому запитанні - так, це некрасиво.

Щодо вашого другого питання - так, ви помиляєтесь . Повернення всього цього коду до основного не дозволяє використовувати його модуляторно як бібліотеку шляхом з'єднання Py_Mainззовні.

Тепер я зрозумів?


Я не питав, чи може це бути довго. Я запитав, чому це не повинно бути довгим.
riwalk

“Складність нижче 10”? Чи є для цього одиниця вимірювання?
стипендіати Доналу

@ Stargazer712 Довжина функції зазвичай регулюється також стандартами кодування. Це питання читабельності (і складності, як правило, довгі функції розгалужені так, що складність значно вище 20), і, як я вже сказав, - mainнічим не відрізняється від будь-якої іншої функції в цьому плані.
littleadv

@Donal - так, натисніть на посилання.
littleadv

Мені доведеться знищити цей один бутон. Ви повністю не вистачаєте наміру питання.
riwalk

0

Ось нова прагматична причина, яка також залишається головною короткою від GCC 4.6.1 журналу змін :

У більшості цілей з іменованою підтримкою розділів функції, що використовуються лише при запуску (статичні конструктори та основні ), функції, які використовуються лише при виході, і функції, виявлені холодними, розміщуються в окремі підрозділи текстового сегмента . Це розширює функцію -freorder-функцій і управляється тим самим комутатором. Мета - покращити час запуску великих програм C ++.

Підсвітка додана мною.


0

Не вважайте, що лише тому, що трохи програмного забезпечення добре, весь код, який стоїть за цим програмним забезпеченням, хороший. Гарне програмне забезпечення та хороший код - це не одне і те ж, і навіть там, де гарне програмне забезпечення підтримується хорошим кодом, неминуче, що у великому проекті знайдуться місця, де стандарти пробують.

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

Але база даних коду Python не виштовхує код для Py_Mainтого, щоб грати в це правило, а тому, що ви не можете експортувати mainз бібліотеки і не називати його функцією.


-1

Вище є декілька технічних відповідей, нехай це залишиться осторонь.

Основна повинна бути короткою, оскільки це має бути завантажувальний пристрій. Основні повинні інстанціювати невелику кількість об'єктів, часто один, які виконують роботу. Як і деінде, ці об'єкти повинні бути добре розроблені, згуртовані, нещільно з'єднані, інкапсульовані, ...

Незважаючи на те, що можуть бути технічні причини, щоб однолінійний головний виклик іншого методу монстра, в принципі ви праві. З точки зору інженерії програмного забезпечення нічого не отримано. Якщо вибір стоїть між головною, що викликає метод монстра, і головним, що є монстровим методом, останній є частково менш поганим.


Ви припускаєте, що "C ++ код повинен використовувати об'єкти та лише об'єкти". Це неправда, C ++ - це багатопарадигмальна мова, і не змушує все формувати OO, як деякі інші мови.
Бен Войгт
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.