Pourquoi main () devrait-il être court?


87

Je programme depuis plus de 9 ans et, selon les conseils de mon premier professeur de programmation, ma main()fonction est toujours extrêmement courte.

Au début, je ne savais pas pourquoi. J'ai juste obéi sans comprendre, pour le plus grand plaisir de mes professeurs.

Après avoir acquis de l'expérience, je me suis rendu compte que si je concevais correctement mon code, il main()me suffisait d' avoir une fonction courte . Ecrire du code modularisé et suivre le principe de responsabilité unique a permis de concevoir mon code par "grappes" et de ne main()servir que de catalyseur pour faire fonctionner le programme.

Il y a quelques semaines, je regardais le code source de Python et j'ai trouvé la main()fonction:

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

...

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

Yay python. main()Fonction courte == Bon code.

Les professeurs de programmation avaient raison.

Voulant regarder plus en profondeur, j'ai jeté un coup d'œil à Py_Main. Dans son intégralité, il est défini comme suit:

/* 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;
}

Bon Dieu tout-puissant ... il est assez grand pour couler le Titanic.

Il semble que Python ait utilisé l'astuce "Introduction à la programmation 101" et ait simplement déplacé main()le code de tout le système vers une fonction différente, appelée quelque chose de très similaire à "principal".

Voici ma question: ce code est-il terriblement écrit ou y a-t-il d'autres raisons d'avoir une fonction principale courte?

En l'état actuel des choses, je ne vois absolument aucune différence entre le faire et simplement Py_Main()réintégrer le code main(). Ai-je tort de penser cela?


4
cela ne serait-il pas mieux pour codereviews.stackexchange.com ?
Foobar

38
@ Louzhin, non. Je ne demande à personne de passer en revue le code source de Python. C'est une question de programmation.
Riwalk

3
TBH, la moitié du code concerne le traitement des options, et chaque fois que votre programme prend en charge de nombreuses options, et que vous écrivez un processeur personnalisé, c'est ce que vous finissez par faire ...
Nim

7
@Star No, Programmers.SE est également destiné aux meilleures pratiques, aux styles de codage, etc. En fait, c'est ce que je visite sur le site.
Mateen Ulhaq

4
@Nim, je comprends c'est ce qu'il fait, mais il n'y a aucune raison de ne pas l' écrire comme options = ParseOptionFlags(argc,argv)optionsest un structqui contient les variables Py_BytesWarningFlag, Py_DebugFlagetc ...
riwalk

Réponses:


137

Vous ne pouvez pas exporter maindepuis une bibliothèque, mais vous pouvez exporter Py_Main. Toute personne utilisant cette bibliothèque peut "appeler" Python plusieurs fois avec des arguments différents dans le même programme. À ce stade, pythondevient juste un autre consommateur de la bibliothèque, un peu plus qu'un wrapper pour la fonction de bibliothèque; il appelle Py_Maincomme tout le monde.


1
Il y a une bonne réponse.
Riwalk

26
Je suppose qu'il serait peut-être plus exact de dire que vous ne pouvez pas l' importer , @Shoosh. La norme C ++ interdit de l'appeler à partir de votre propre code. En outre, son lien est défini par la mise en œuvre. Vous pouvez également renvoyer des mainappels exit, ce que vous ne voulez généralement pas faire par une bibliothèque.
Rob Kennedy

3
@Coder, voir C ++ 03 §3.6.1 / 5: "Une instruction return maina pour effet de laisser la fonction principale… et d'appeler exitavec la valeur de retour comme argument." Voir également les § 18.3 / 8, qui expliquent que "les objets dont la durée de stockage est statique sont détruits" et que "tous les flux C ouverts… sont vidés" lors de l'appel exit. C99 a un langage similaire.
Rob Kennedy

1
@Coder, que les exitfeuilles mainsoient sans importance. Nous ne discutons pas du comportement de exit. Nous discutons du comportement de main. Et le comportement de main inclut le comportement de exit, quel qu'il soit. C'est ce qui rend indésirable l'importation et l'appel main(si cela est même possible ou autorisé).
Rob Kennedy

3
@Coder, si le retour de mainn'a pas pour effet d'appeler exitvotre compilateur, celui-ci ne suit pas la norme. Que la norme dicte un tel comportement pour mainprouve qu'il est quelque chose de spécial à ce sujet. La particularité mainest que le retour de celui-ci a pour effet d'appeler exit. ( Comment cela se fait-il aux rédacteurs du compilateur? Le compilateur pourrait simplement insérer du code dans l'épilogue de fonction qui détruit les objets statiques, appelle des atexitroutines, vide les fichiers et termine le programme - ce qui, encore une fois, n'est pas ce que vous voulez dans une bibliothèque .)
Rob Kennedy

42

Ce n'est pas que ça ne maindevrait pas être long, mais éviter que toute fonction soit trop longue. mainest juste un cas particulier de fonction. Les fonctions plus longues deviennent très difficiles à maîtriser, réduisent la facilité de maintenance et sont généralement plus difficiles à utiliser. En gardant les fonctions (et main) plus courtes, vous améliorez généralement la qualité de votre code.

Dans votre exemple, supprimer le code ne présente aucun avantage main.


9
Le mot d'or peut être "réutiliser". Un long mainn'est pas très réutilisable.
S.Lott

1
@S - C'est un mot d'or. Un autre est OMG !!! Le TDAH vient d'entrer !!!! ou en termes simples: lisibilité.
Edward Strange

3
main () a aussi des restrictions que d’autres fonctions n’ont pas.
Martin York

1
De plus, main () n'a pas de signification réelle. Votre code devrait tous signifier quelque chose pour un autre programmeur. J'utilise main pour analyser les arguments et c'est tout - et je le délègue même si c'est plus que quelques lignes.
Bill K

@ Bill K: Bon point, utiliser main () uniquement pour analyser les arguments (et lancer le reste du programme) est également conforme au principe de responsabilité unique.
Giorgio

28

Une des raisons de faire main()court implique des tests unitaires. main()est la seule fonction qui ne peut pas être testée par unité, il est donc logique d'extraire la majorité du comportement dans une autre classe pouvant être testée par unité. Cela va avec ce que vous avez dit

L'écriture de code modularisé et le respect du principe de responsabilité unique ont permis de concevoir mon code par "groupes", et main () n'a servi que de catalyseur pour faire fonctionner le programme.

Note: j'ai eu l'idée d' ici .


Un autre bon. Jamais pensé à cet aspect.
Riwalk

16

C'est rarement une bonne idée maind'être long; comme avec n'importe quelle fonction (ou méthode) si elle est longue, vous manquez probablement des opportunités de refactoring.

Dans le cas spécifique que vous avez mentionné ci-dessus, il mainest court car toute cette complexité est prise en compte Py_Main; si vous voulez que votre code se comporte comme un shell Python, vous pouvez simplement utiliser ce code sans trop de manipulations. (Cela doit être pris en compte comme ça parce que cela ne fonctionne pas bien si vous mettez maindans une bibliothèque; des choses étranges se produisent si vous le faites.)

EDIT:
Pour clarifier, mainne peut pas être dans une bibliothèque statique car elle n’a pas de lien explicite avec elle et ne sera donc pas liée correctement (sauf si vous colocalisez-le dans un fichier objet avec quelque chose qui est mentionné, ce qui est horrible !) Les bibliothèques partagées sont généralement traitées comme étant similaires (encore une fois, pour éviter toute confusion), bien que sur de nombreuses plates-formes, un facteur supplémentaire réside dans le fait qu’une bibliothèque partagée n’est qu’un exécutable sans section d’amorçage (dont mainla partie la plus visible est la plus visible). ).


1
En bref, ne mettez pas maindans une bibliothèque. Ca ne marchera pas ou ça vous dérangera terriblement. Mais déléguer la quasi-totalité de son travail à une fonction qui se trouve dans une bibliothèque est souvent judicieux.
Donal Fellows

6

Main doit être court pour la même raison que toute fonction doit être courte. Le cerveau humain a du mal à conserver en mémoire de grandes quantités de données non partitionnées. Découpez-le en morceaux logiques pour qu'il soit facile pour les autres développeurs (ainsi que pour vous-même!) De digérer et de raisonner.

Et oui, votre exemple est terrible et difficile à lire, encore moins à maintenir.


Oui, j'ai toujours soupçonné que le code lui-même était terrible (bien que la question concernait l'emplacement du code, pas le code lui-même). Je crains que ma vision de Python ne soit altérée de manière inhérente à la suite ...
riwalk

1
@ stargazer: Je ne sais pas si le code lui-même est terrible, mais simplement qu'il n'est pas bien organisé pour la consommation humaine. Cela dit, il y a beaucoup de code "laid" qui fonctionne bien et qui fonctionne très bien. La beauté du code ne fait pas tout, même si nous devons toujours faire de notre mieux pour écrire le code le plus propre possible.
Ed S.

meh Pour moi, ils ne font qu'un. Le code propre a tendance à être plus stable.
Riwalk

Le code n'est pas terrible, il y a principalement des boîtiers de commutation et le traitement de plusieurs plates-formes. Qu'est-ce que vous trouvez terrible?
Francesco

@ Francesco: Désolé, "Terrible" du point de vue de la maintenance et de la lisibilité, pas du point de vue fonctionnel.
Ed S.

1

Certaines personnes apprécient plus de 50 fonctions qui ne font rien d’autre, mais qui encapsulent l’appel d’une autre fonction. Je préférerais plutôt la fonction principale normale qui effectue la logique du programme principal. Bien structuré bien sûr.

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

Je ne vois aucune raison pour laquelle je devrais envelopper quoi que ce soit à l'intérieur d'une enveloppe.

C'est purement un goût personnel.


1
Parce que c'est le code. Vous pouvez écrire du code de cette façon sans avoir à (presque) jamais écrire de commentaires. Et lorsque vous modifiez le code, la documentation change automatiquement :-).
Oliver Weiler

1

Il est recommandé de garder TOUTES vos fonctions courtes, pas seulement principales. Cependant, "court" est subjectif, cela dépend de la taille de votre programme et du langage que vous utilisez.


0

Il n'est pas nécessaire maind'être d'une longueur quelconque, autre que les normes de codage. mainest une fonction comme une autre, et en tant que telle, sa complexité devrait être inférieure à 10 (ou quoi que disent vos normes de codage). Ça y est, toute autre chose est plutôt argumentative.

modifier

mainne devrait pas être court. Ou long. Il doit inclure les fonctionnalités qu'il est nécessaire d'exécuter en fonction de votre conception et respecter les normes de codage.

En ce qui concerne le code spécifique dans votre question - oui, c'est moche.

En ce qui concerne votre deuxième question - oui, vous avez tort . Remettre tout ce code dans main ne vous permet pas de l'utiliser comme bibliothèque en liant Py_Maindepuis l'extérieur.

Maintenant, je suis clair?


Je n'ai pas demandé si ça pouvait être long. J'ai demandé pourquoi cela ne devrait pas être long.
Riwalk

"Complexité inférieure à 10"? Existe-t-il une unité de mesure pour cela?
Donal Fellows

@ Stargazer712 La durée des fonctions est généralement également régie par les normes de codage. Il s’agit d’un problème de lisibilité (et de complexité, les fonctions les plus longues sont généralement ramifiées de manière à ce que la complexité soit bien supérieure à 20), et comme je l’ai dit - mainn’est pas différent de toute autre fonction à cet égard.
Littleadv

@Donal - oui, cliquez sur le lien.
Littleadv

Je vais devoir réduire ce bourgeon. Vous manquez complètement l'intention de la question.
Riwalk

0

Voici une nouvelle raison pragmatique pour rester trop court du Changelog GCC 4.6.1 :

Sur la plupart des cibles prenant en charge les sections nommées, les fonctions utilisées uniquement au démarrage (constructeurs statiques et main ), les fonctions utilisées uniquement à la sortie et les fonctions détectées froides sont placées dans des sous-sections de segment de texte distinctes . Ceci étend la fonctionnalité des fonctions -freorder et est contrôlé par le même commutateur. L'objectif est d'améliorer le temps de démarrage de grands programmes C ++.

Mise en surbrillance ajoutée par moi.


0

Ne supposez pas que, simplement parce qu’un logiciel est bon, tout le code qui le sous-tend est bon. Un bon logiciel et un bon code ne sont pas la même chose et même lorsqu'un bon logiciel est soutenu par un bon code, il est inévitable que dans un grand projet, il y ait des endroits où les normes glissent.

C'est une bonne pratique d'avoir une mainfonction courte , mais il ne s'agit en réalité que d'un cas particulier de la règle générale selon laquelle il est préférable d'avoir des fonctions courtes. Les fonctions courtes sont plus faciles à comprendre et à déboguer, tout en permettant de mieux s'en tenir au type de conception «à usage unique» qui rend les programmes plus expressifs. mainC’est peut-être un lieu plus important de respecter la règle puisque quiconque veut comprendre le programme doit comprendre, mainalors que des recoins plus obscurs de la base de code peuvent être visités moins souvent.

Cependant, la base de code Python ne pousse pas le code Py_Mainpour jouer cette règle, mais parce que vous ne pouvez pas exporter maindepuis une bibliothèque ni l'appeler en tant que fonction.


-1

Il y a plusieurs réponses techniques ci-dessus, laissez cela de côté.

Une main devrait être courte parce que ce devrait être un bootstrap. Le principal doit instancier un petit nombre d'objets, souvent un, qui font le travail. Comme partout ailleurs, ces objets doivent être bien conçus, cohérents, faiblement couplés, encapsulés, etc.

Bien qu’il puisse y avoir des raisons techniques à avoir une ligne principale pour appeler une autre méthode monstre, en principe, vous avez raison. Du point de vue du génie logiciel, rien n’a été gagné. Si vous avez le choix entre une ligne principale appelant une méthode monstre, et main étant elle-même une méthode monstre, cette dernière est légèrement moins mauvaise.


Vous supposez que "le code C ++ doit utiliser des objets et uniquement des objets". Ce n'est pas vrai, C ++ est un langage multiparadigmatique et ne force pas tout dans un moule OO comme dans d'autres langages.
Ben Voigt le
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.