1
votes

Quel est le but de créer la classe qui instancie toutes les autres classes et sous-systèmes

Après avoir examiné de nombreux projets open source, j'ai remarqué que beaucoup d'entre eux utilisent la même idée pour l'initialisation et la désinitialisation de l'ensemble du système.

Beaucoup d'entre eux ont une classe spécifique (probablement un singletone) qui charge les ressources, instancie toutes les autres classes, sous-systèmes, les connecte les uns aux autres et prépare la structure des objets qui est utilisée pendant l'exécution. Pourquoi ne pas faire cela dans main () ?

Quelle idée se cache derrière cette approche? S'agit-il d'un modèle de programmation de haut niveau qui présente de nombreux avantages?


10 commentaires

Pouvez-vous citer quelques projets qui font cela?


On dirait que «tout doit être une classe, même les choses qui n'ont aucune affaire étant une classe».


D'après la description, cela ressemble à un objet gestionnaire . Ce qui est étroitement lié au modèle de médiateur .


Parce que le faire dans main est "orienté objet".


A titre d'exemple, vous pouvez jeter un œil au projet OpenSCADA. c'est main.cpp instancie la classe TSYS qui effectue toutes les autres activités d'initialisation . Pour autant que je sache, la documentation de Qt recommande également un tel style


Tout code dans main () doit y être mis manuellement par le programmeur qui utilise la bibliothèque. Cela signifie plus de travail pour ce programmeur, et plus important encore, cela présente une opportunité pour ce programmeur d'oublier d'ajouter ce code ou de le faire mal, ce qui pourrait entraîner un problème de support pour les développeurs de la bibliothèque. Par conséquent, tout ce que les développeurs de la bibliothèque peuvent faire pour que tout «fonctionne» sans exiger une action explicite du code appelant est un avantage pour la convivialité.


@JeremyFriesner mais l'utilisation de la classe dédiée ne résout pas le problème de l'ajout manuel du code. Nous ajoutons simplement cela à cette implémentation de classe au lieu de main () Je pense que l'idée principale de créer une telle classe (appelons-la Initializer) est de la réutiliser dans les projets suivants et d'encapsuler toute l'activité d'initialisation en un seul endroit et de garder main.cpp propre


@LeonidP il est vrai que les implémenteurs de bibliothèques doivent encore ajouter le code, juste à un autre emplacement de la bibliothèque. Mais ils n'ont qu'à ajouter ce code une seule fois, et depuis qu'ils ont écrit la bibliothèque, ils savent probablement ce qu'ils font. Pour les 10 000 développeurs tiers qui souhaitent utiliser la bibliothèque (et qui ne comprennent probablement pas très bien ses composants internes, puisqu'ils ne l'ont pas écrite), cela rend la vie beaucoup plus facile s'ils ne le font pas. t doivent connaître des informations détaillées sur la façon de tout configurer.


@JeremyFriesner oui, je suis absolument d'accord avec vous si nous parlons des bibliothèques et de leur initialisation. Mais je ne parlais pas des bibliothèques dans la question principale. Je parlais de l'application logicielle et de sa procédure d'initialisation qui est claire pour les développeurs de cette application.


Concevoir une application logicielle avec l'anticipation qu'un jour vous (ou quelqu'un d'autre) voudrez utiliser le code comme bibliothèque n'est jamais une mauvaise idée.


4 Réponses :


0
votes

Garder les modules de fonction courts aide à la clarté et au débogage, il est donc préférable que le module principal reste court.

Factory Pattern, enregistre les objets lors de l'initialisation, qui à son tour génère des objets plus tard dans le programme. Ce modèle convient également si de nouveaux objets doivent être intégrés dans votre système, par exemple un nouveau plugin


0 commentaires

0
votes

Cette classe est appelée "Composition Root" et est utilisée avec des modules logiciels prenant en charge l'injection de dépendances.

Cela permet aux parties d'être indépendantes les unes des autres en rassemblant les dépendances entre elles dans un seul module séparé qui définit l'application dans son ensemble.

Commencez probablement ici: Qu'est-ce qu'un racine de composition dans le contexte de l'injection de dépendances


2 commentaires

Mais les dépendances peuvent également être rassemblées par un main qui n'appelle pas le constructeur d'une classe de gestionnaire.


Merci d'avoir mentionné le motif de la racine de la composition. J'en ai entendu parler lors de la lecture d'articles sur l'injection de dépendances, mais je n'ai pas examiné ce modèle de cette manière. Oui, il peut être utilisé comme classe principale qui instancie tous les autres sous-systèmes



2
votes

Il y a plusieurs raisons d'avoir une classe de manager:

  • Cela rend les choses un peu plus cohérentes. Si le reste de votre projet est écrit en code de style POO, avoir la classe principale comme ça aussi est juste un peu plus agréable.
  • Cela facilite les tests dans de nombreux cas. main ne peut pas être appelé en C ++; si tout est dans le constructeur d'une classe, vos tests peuvent l'appeler assez librement.
  • Il vous permet d'avoir plusieurs systèmes "globaux" en même temps. De toute évidence, l'utilité de cela dépend du projet, mais il n'est pas déraisonnable, par exemple, d'exécuter deux systèmes en parallèle pour assurer la cohérence.
  • Il peut présenter une API plus simple pour interagir avec la bibliothèque, de sorte que les fonctions qui affectent plusieurs sous-systèmes peuvent être une méthode sur la classe de gestionnaire au lieu d'une fonction libre prenant un paramètre pour chaque sous-système. Ceci est particulièrement utile dans quelque chose comme une bibliothèque graphique ou un moteur de jeu, où une action simple (par exemple, ajouter un bouton) peut avoir besoin d'être enregistrée avec plusieurs sous-systèmes pour fonctionner correctement (rendu, gestion des entrées de souris , gestion de la saisie au clavier).
  • Cela peut faciliter l'échange de sous-systèmes avec d'autres backends. Par exemple, si vous avez un backend qui rend avec OpenGL et un autre qui rend avec DirectX, vous pouvez simplement leur faire implémenter la même interface de "sous-système de rendu" et changer le système entier une fois, au moment de la compilation, au niveau de l'ensemble programme, sans avoir à changer chaque mention ou à utiliser des typedef et à compter sur des personnes incluant les en-têtes corrects.

Ceux qui s'appliquent dépendent du projet et de ses objectifs, bien sûr, ainsi que d'autres éléments de son architecture. Si vous êtes curieux de savoir pourquoi une bibliothèque en particulier l'a fait comme elle l'a fait, vous devriez demander aux responsables de cette bibliothèque.


5 commentaires

C'est plutôt 'main () n'est pas censé être appelé' que 'main () ne peut pas être appelé' .


@nada Il peut être appelé de la même manière que NULL peut être déréférencé.


Eh bien, il y a une différence de théorie dans la pratique. En pratique, il existe au moins 1 compilateur (commun!), Qui permet d'appeler main, même s'il n'est pas conforme au standard. Mais quel compilateur est 100% conforme à la norme? Avec le déréférencement de nullptrs, c'est différent - cela ne «fonctionne» ni en théorie ni en pratique.


@nada En pratique, il y a pas mal de situations où le déréférencement NULL est non seulement un code valide, mais extrêmement utile (par exemple, des systèmes embarqués où 0 est une adresse réelle). Donc oui. Vous pouvez appeler main de la même manière que vous pouvez déréférencer NULL: si vous voulez que votre code soit conforme à la norme, vous ne pouvez pas, mais si vous êtes prêt à vous verrouiller sur une implémentation spécifique et peut-être même la plate-forme cible, alors c'est possible, et peut même être utile.


Si vous ressentez le besoin d'appeler manuellement main () , vous pouvez également éviter un comportement indéfini en renommant main () en main_aux () ( ou similaire), et l'appelant à la place. (et, bien sûr, ajoutez un nouveau main () qui appelle simplement main_aux () , afin que votre programme puisse toujours compiler et s'exécuter). Et une fois que vous avez fait cela, vous trouverez peut-être utile d'aller plus loin et de mettre à niveau main_aux () pour devenir un objet de classe afin qu'il puisse avoir des méthodes et des variables membres, etc. . et puis vous êtes arrivé à la conception sur laquelle l'interrogateur a posé la question :)



0
votes

Quelle idée se cache derrière cette approche? S'agit-il d'un modèle de programmation de haut niveau qui présente de nombreux avantages?

La partie analyse de ceci est parfois appelée un cas d'utilisation (fonctionnalité) appelé «Démarrage». Le livre de Craig Larman sur OOAD l'appelle le objet de domaine initial idiome (pas tout à fait un modèle):

Comment les applications démarrent-elles?

L'opération système startUp ou initialize d'un cas d'utilisation Start Up représente de manière abstraite la phase d'initialisation de l'exécution lorsqu'une application est lancée. Pour comprendre comment concevoir un diagramme d'interaction pour cette opération, vous devez d'abord comprendre les contextes dans lesquels l'initialisation peut se produire. La manière dont une application démarre et s'initialise dépend du langage de programmation et du système d'exploitation.

Dans tous les cas, un idiome de conception courant consiste à créer un objet de domaine initial ou un ensemble d'objets de domaine initial homologues qui sont les premiers objets de «domaine» logiciel créés. Cette création peut se produire explicitement dans la méthode de départ main ou dans un objet Factory appelé depuis la méthode main .

Souvent, l'objet de domaine initial (en supposant le cas singulier), une fois créé, est responsable de la création de ses objets de domaine enfant directs. Par exemple, un Store choisi comme objet de domaine initial peut être responsable de la création d'un objet Register .

Dans une application Java, par exemple, la méthode main peut créer l'objet de domaine initial ou déléguer le travail à un objet Factory qui le crée.

Il donne plus tard les instructions suivantes pour choisir l'objet de domaine:

Choisissez comme objet de domaine initial une classe à ou près de la racine de la hiérarchie de confinement ou d'agrégation des objets de domaine. Il peut s'agir d'un contrôleur de façade, tel que Register, ou d'un autre objet considéré comme contenant la totalité ou la plupart des autres objets, comme un magasin.

Ce dernier fait référence au modèle de domaine pour une application de point de vente, où Store et Register sont des classes.


2 commentaires

Merci pour une réponse aussi complète. Je n'ai jamais entendu parler de l ' objet de domaine initial mais il semble que je pose la question.


@LeonidP. Bienvenue dans stackoverflow. Si vous aimez les réponses (et que vous avez suffisamment de réputation), vous pouvez les voter ou les accepter si c'est la réponse qui, selon vous, satisfait votre question.