IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

 C++ Discussion :

Amélioration design code


Sujet :

C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre actif Avatar de Sytten
    Homme Profil pro
    Étudiant
    Inscrit en
    Janvier 2012
    Messages
    72
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2012
    Messages : 72
    Par défaut Amélioration design code
    Bonjour,

    Après avoir fini mon jeu (3500 lignes de code environ), je me rend compte qu'il me manque des connaissances en design de code. Ça peut paraître assez vague je sais, mais c'est le but. Je veux en apprendre plus sur comment bien designer son code afin qu'il soit flexible, solide et facilement maintenable et améliorable. Les principes de POO (j'ai lu les articles d'Emmanuel Deloget) sont très bons, je n'ai vu jusqu'à maintenant que certains petits exemples.
    J'aimerais donc savoir s'il vous me conseilleriez certains livres ou tutoriels. Je sais qu'une bonne façon d'apprendre à bien coder est de lire du bon code, alors je me demande si vous avez des codes sources auxquels je pourrait jeter un coup d'oeil...
    Si vous avez d'autres conseils, n'hésitez pas à me les donner
    Merci de votre aide,
    Sytten

  2. #2
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Salut,

    Avec une question aussi générique, il ne va pas être évident de répondre de manière précise

    De manière générale, il faut impérativement
    • Essayer d'exprimer tes besoins le plus précisément possible, en veillant à définir les besoins (toujours le plus précisément possible) qui apparaissent en cours de route (soit parce qu'ils apparaissent dans la description d'un besoin, soit parce que tu te dis que "tiens, ce serait pas mal si ..." )
    • Essayer de respecter scrupuleusement les cinq piliers de la programmation orientée objets connus sous l'acronyme SOLID
    • garder en mémoire le fait que tous les problèmes en informatiques peuvent être résolus par une indirection supplémentaire
    • Ce souvenir que l'optimisation prématurée est la route privilégiée vers l'enfer
    • Se souvenir que la solution la plus simple est toujours la moins compliquée (heu, pardon, la meilleure)
    • Ne jamais considérer une partie de ta conception comme "finie" ou "définitive" (toujours se laisser l'occasion de "revenir sur un point particulier" en fonction des problèmes ou des nouvelles idées que l'on rencontre en cours de développement)
    • idéalement, utiliser un système de gestion de version concurrente (SVN Git ...) afin de pouvoir "revenir en arrière" si une modification vient à "tout casser"
    • respecter des règles de nommage et de codage (indentation, retour à la ligne, ...) strictes
    • idéalement mettre au point une politique stricte de tests unitaires: Si tu crées dés le départ des tests unitaires sur les fonctions les plus simples, et que tu continues en testant systématiquement toutes les fonctions, tu auras beaucoup plus facile à repérer les endroits où une modification parfois "mineure" provoque une régression
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  3. #3
    Membre actif Avatar de Sytten
    Homme Profil pro
    Étudiant
    Inscrit en
    Janvier 2012
    Messages
    72
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2012
    Messages : 72
    Par défaut
    Salut,

    Merci Koala, ça confirme pas mal ce que je pensais et ça vient renforcer mon idée.

    Maintenant je peux poser des questions un peu plus précise:
    - Dans le cas d'un jeu, par exemple, serait-il acceptable d'avoir un objet personnage qui ne contient seulement que des données sur un personnage (avec les accesseurs si on veut que ça serve à quelque chose...)?

    - Au niveau des accesseurs, comment éviter d'utiliser des setters et getters? (j'ai souvent ce problème dans mon code)

    garder en mémoire le fait que tous les problèmes en informatiques peuvent être résolus par une indirection supplémentaire
    - Mais encore? Un exemple, car j'avoue ne pas trop saisir...

    Ce souvenir que l'optimisation prématurée est la route privilégiée vers l'enfer
    - Hum, à bien y penser c'est bien vrai... ça remet certaines choses en perspective.

    - Pour les règles de nommage et codage, je sais que c'est souvent au choix du programmeur, mais y a-t-il des façons meilleures que d'autres afin de rendre le code le plus lisible possible (un genre de standard...)?

    - Je connais l'utilité de faire des tests unitaires, mais je ne sais pas vraiment comment les implémenter réellement dans mon code. Tu aurais des tutos à me recommander?

    - Question que je me pose depuis un moment, comment séparer la partie graphique d'un programme de la partie logique? Le modèle MVC est-il une bonne piste? Qu'en est-il des jeux vidéos? Peut-on correctement implémenter un renderer indépendant (pour qu'on ne soit pas obliger de trainer partout les objets que l'on doit afficher à l'écran)?

    Beaucoup de questions encore, mais elles tournent dans ma tête depuis un moment...

    Merci beaucoup de ton aide,
    Sytten

  4. #4
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Citation Envoyé par Sytten Voir le message
    Maintenant je peux poser des questions un peu plus précise:
    - Dans le cas d'un jeu, par exemple, serait-il acceptable d'avoir un objet personnage qui ne contient seulement que des données sur un personnage (avec les accesseurs si on veut que ça serve à quelque chose...)?
    Ce serait sans doute acceptable, mais surement pas pour un objet de type personnage, et ce ne serait de toutes manières pas très OO.

    Il faut comprendre que C++ autorise l'utilisation de trois paradigmes:

    Le paradigme "impératif", qui est composé de structures de données brutes (sans fonction membres, uniquement des fonctions libres ) et qui va se concentrer sur les données manipulées. C (et bien d'autres ) est un langage impératif.

    Ensuite, il y a le paradigme orienté objets.

    Ce paradigme va s'intéresser en priorité aux comportements des objets, aux services que l'on est en droit d'attendre de leur part.

    Les données que les objets manipulent deviennent "secondaires" et ne sont là que pour... permettre à l'objet de rendre les services que l'on en attend.

    Contrairement à ce qu'ont pu dire les gens lorsque ce paradigme est apparu, son principal attrait n'est pas de permettre l'encapsulation (il est tout à fait possible d'obtenir une certaine encapsulation en langage impératif: la structure FILE de C en est un parfait exemple ) mais de fournir la substituabilité.

    Le principe de base à respecter en Orienté Objets est donc le fameux Principe de Substitution de Liskovt (LSP de son "petit nom"), qui est en quelque sorte la "pierre angulaire" sur lequel se basent l'héritage et le polymorphisme.

    Enfin C++ propose le paradigme générique: il dit que, si l'on peut parfaitement ignorer le type réel des donnés que l'on va manipuler, il est tout à fait possible de savoir comment nous voudrons les manipuler.

    Une liste chainée, par exemple, va s'utiliser de manière strictement identique si elle contient des entiers, des chaines de caractères ou des véhicule ou du blougi boulga

    Il est donc tout à fait possible de prévoir les comportements et les services que nous attendrons de la part de la liste chainée en attendant de savoir le type de données qui seront manipulées.

    En attendant, nous aurons choisi un type tout à fait générique (T Type ou encore ToutTypeQuiPeutEtreQuelqueChose ) afin de représenter les données qui seront manipulées
    - Au niveau des accesseurs, comment éviter d'utiliser des setters et getters? (j'ai souvent ce problème dans mon code)
    Attention: accesseur == getter (ca donne accès à une données), setter == "mutateur" (ca permet de modifier une donnée)

    Ce n'est qu'un détail, mais autant utiliser le bon noms pour parler des choses

    Ceci étant posé, Il faut savoir que, en plus des cinq piliers dont j'ai parlé plus haut, il y a un sixième "dieu" qui entre en ligne de compte: la loi dite de demeter.

    Cette loi nous fais remarquer que, si l'on manipule un objet de type A qui utilise en interne un objet de type B, nous ne devrions pas forcément connaitre le type B pour pouvoir manipuler (et surtout le modifier) notre objet de type A (le lien que je m’apprête à donner te permettra de comprendre ).

    Du coup, tu ne devrais déjà fournir un accesseur sur une donnée membre de ta classe que si cela correspond effectivement à un service que tu es en droit d'attendre de la part de celle ci ( on s'attend, par exemple, à ce qu'une classe Personne soit capable de répondre à la question "quel est ton nom", et il est donc "logique" d'avoir un accesseur sur le membre en question )

    A priori, nous aurons donc sans doute beaucoup moins d'accesseurs que de membres dans une classe donnée, même s'il y a de grandes chances que l'on se retrouve avec beaucoup plus de fonctions renvoyant des valeurs (calculées ou déduites) que de membres de la classe

    Mais la présence d'un mutateur va entièrement à l'encontre de la loi de déméter, car il oblige à connaitre exactement le type de la données que l'on veut modifier.

    De plus, setXXX ne correspond à aucun service particulier (ou, plutôt, correspond à un service beaucoup trop générique )

    Il faudrait donc préférer fournir un ensemble de services qui correspondent à la manière dont on veut pouvoir modifier l'état de notre objet.

    Si un objet présente la caractéristique d'être positionné (qu'il manipule en interne un point 3 dimensions) par exemple, nous préférerons sans doute avoir une fonction "moveTo" prenant un point 3 dimensions et / ou une fonction "move" qui prend trois différences à appliquer à la position actuelle de l'objet (une pour chaque dimension) parce que cela correspond beaucoup mieux aux services rendus que "setPosition" d'une part, et que l'on se doute bien que le fait de déplacer l'objet peut ne pas uniquement avoir comme résultat... de modifier sa position.
    - Mais encore? Un exemple, car j'avoue ne pas trop saisir...
    J'ai présenté l'exemple rêvé lors d'une de mes interventions.

    Elle est essentiellement orientée vers le paradigme générique, mais elle devrait te permettre de comprendre bien des choses (d'autant plus que la première partie parle du paradigme orienté objet et donne un exemple concret de mise en oeuvre de la loi de demter )
    - Hum, à bien y penser c'est bien vrai... ça remet certaines choses en perspective.
    Je peux te parler d'expérience en te disant qu'il est déjà "suffisamment difficile" d'obtenir un code
    1. qui compile sans erreur ni avertissement (car les avertissements signalent généralement que le code peut "mener à des catastrophes" dans certaines circonstances particulières )
    2. fonctionne correctement (comprend: fait ce que l'on attend de lui, quelles que soient les circonstances)
    3. reste ne serait-ce que *relativement* lisible
    Or, ce n'est que lorsque tu es arrivé à obtenir ces trois points que tu peux envisager d'éventuelles optimisations, et uniquement si tu éprouves de réels problèmes de performances.

    Et encore!!!
    • il ne sert à rien de perdre son temps à essayer de gagner quelques milli secondes sur une fonction qui n'est appelée qu'une fois.
    • Les optimisations les plus spectaculaires sont celles qui ont trait à l'algorithme utilisé
    • une optimisation n'est justifiée que s'il est prouvé qu'elle apporte un gain significatif (ce qui implique qu'il faut pouvoir faire des mesures avant / après)

    - Pour les règles de nommage et codage, je sais que c'est souvent au choix du programmeur, mais y a-t-il des façons meilleures que d'autres afin de rendre le code le plus lisible possible (un genre de standard...)?
    Malheureusement, non.

    Il est possible de se rendre compte que certaine règles identiques sont suivies à de nombreux endroits, voire, que l'on pourrait peut etre les qualifier de "standard de fait" (sauf que ce ne serait de toutes manières pas un standard réel) mais il n'existe aucun standard "officiel" à ce point de vue.

    Ce qui importe surtout, c'est de les respecter non seulement au niveau des fichiers, mais surtout au niveau du projet pour qu'elles soient cohérente d'un fichier à l'autre
    - Je connais l'utilité de faire des tests unitaires, mais je ne sais pas vraiment comment les implémenter réellement dans mon code. Tu aurais des tutos à me recommander?
    Je pourrais surement te recommander des tutoriels spécifiques à certaines bibliothèques de tests unitaires, mais ils sont essentiellement orientés vers... l'utilisation de la bibliothèque en question.

    Or, je présumes que tu voudrais plutôt un tutoriel sur les tests unitaires de manière générale Je t'avouerai que je n'en connais aucun, mais qui sait, peut etre quelqu'un dispose-t-il du lien qui fera ta joie (et la mienne) .

    Cependant, tu peux partir du principe que les tests unitaires ne sont intéressants que dans la mesure où il permettent de prouver que tu n'a pas réussi à prendre la logique d'une fonction en défaut.

    L'intérêt d'un test unitaire qui prend le cas qui devrait fonctionner quoi qu'il arrive (par exemple 6/2 est bel et bien égale à 3 ) est donc beaucoup moins important que celui du test qui risque de poser problème :que se passe-t-il si j'essaye de diviser 6 par 0 ... Et si j'introduis "ABC" au lieu de 6 car celui qui risque de poser problème te prouvera que ta fonction est en mesure de résister aux erreurs.

    Maintenant, il est souvent difficile de prévoir strictement tous les "cas limites" dés le départ.

    Chaque fois que tu sera confronté à un bug, il sera utile de créer un test unitaire qui reproduise les circonstances dans lesquelles il se produit.

    Evidemment, la correction du bug devra veiller à ce que le bug ne se reproduise plus, mais surtout (et c'est souvent le plus difficile ) à ne pas casser le reste des tests unitaires qui montrent que l'application n'est pas prise en défaut

    - Question que je me pose depuis un moment, comment séparer la partie graphique d'un programme de la partie logique? Le modèle MVC est-il une bonne piste? Qu'en est-il des jeux vidéos? Peut-on correctement implémenter un renderer indépendant (pour qu'on ne soit pas obliger de trainer partout les objets que l'on doit afficher à l'écran)?
    C'est parfaitement faisable, et d'ailleurs chaudement recommandé.

    Il faut "juste" être conscient que cela va augmenter drastiquement le nombre de types qu'il te faudra gérer (un rapport de 1 pour 3 semble le minimum) car, pour chaque élément de ton modèle (ta partie métier), tu risque de te retrouver avec un (ou plusieurs) éléments dans la partie contrôleur et sans doute autant dans la partie vue.

    Cependant, ce n'est qu'une application stricte du principe de la responsabilité unique (SRP dans solid )
    Beaucoup de questions encore, mais elles tournent dans ma tête depuis un moment...
    Quand elle seront arrivées à "maturation", n'hésites pas à venir les poser
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  5. #5
    Membre actif Avatar de Sytten
    Homme Profil pro
    Étudiant
    Inscrit en
    Janvier 2012
    Messages
    72
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2012
    Messages : 72
    Par défaut
    Wow super trop top
    Enfin ça répond à beaucoup de questions sans réponse.
    Merci de ces explications très détaillées et du soin apporté à ta réponse, elle a vraiment été utile!

    - Je comprend maintenant vraiment pour ce n'est pas bon de faire une classe personnage, maintenant je vais essayer de voir les alternatives sûrement en utilisant un modèle MVC...

    - Voir tout comme étant un service est vraiment une base qu'il me manquait. Ça démontre clairement que les setter ne sont pas une bonne chose pour l'OO...

    Tu ne devrais donc pas te retrouver face à ne serait ce que la possibilité de faire voiture.getReservoir().fill() ( et surtout pas sous cette forme, vu qu'un accesseur devrait toujours renvoyer un objet constant ) ni voiture.getReservoir().getMaxQuantity() par contre, tu fera sans doute quelque chose comme voiture.addCarburant(quantity) qui aura pour effet de... rajouter quantity au réservoir ou voiture.tankState()qui aura pour effet de renvoyer le rapport entre la quantité de carburant qu'il reste dans le réservoir et la capacité maximale de celui-ci
    -Exactement mon problème! Même syntaxe à quelques lettres près^^ Juste ça vient de m'ouvrir les yeux sur cette loi
    - Pour le reste de l'exemple, j'avoue que je ne dispose pas encore de toutes les connaissances nécessaires pour le comprendre (notamment au niveau des template), mais je vais certainement le relire dans un avenir rapproché (puisque je dois assimilé ces connaissances avant mon prochain projet qui sera plus ambitieux...)

    - La constance dans les règles de nommage est bien évidemment de rigueur, j'ai lu un article l'autre jour sur les règles des devs de google (http://google-styleguide.googlecode....k/cppguide.xml). Ça peut être un début, surtout que je commence à avoir certaines règles moi-même. Il va juste être important, j'imagine, que je définisse des règles claires et précises avec ma team avant le début de notre projet...

    - Dans le cas des tests unitaires, il faut donc ajouter certaines lignes de code si je comprend bien le temps du test. Cela peut se faire un debug mode (afin de voir en direct la modification des données dans la fonction). Il m'est arrivé d'avoir des bugs dans mon jeu lors d'une division par 0 ou par moins que 1 dans le calcul des déplacements. Je peux donc facilement imaginé l'utilité de ces tests. Question qui me vient comme ça: vaut-il la peine de prendre de le temps d'en faire un/plusieurs pour chaque nouvelle fonction? ou seulement les fonctions critiques (comprendre les plus importantes) méritent qu'on en fasse...

    - Si on n'utilise pas ce fameux modèle MVC, y a-t-il d'autres façon de coder correctement un jeu en respectant les principes? (Afin de ne pas se retrouver avec des gods objects notamment...)

    - Dans le cas d'un MVC, comment les classes PersonnageControler, PersonnageView et Personnage, par exemple, doivent-elle interagir entre elles? Un lien d'amitié serait-il approprié ou doit-on passer par des fonctions membres?

    - Dans un cas concret de mon jeu, j'ai une fonction dig dans laquelle il y a une boucle le temps du creusage du bloc (notamment pour ne pas que le joueur fasse d'autres actions durant le creusage). Pourtant, je dois continuer à afficher mes éléments dans cette boucle. La solution que j'ai utilisé (et que je n'aime pas puisque qu'elle est mauvaise et peu flexible) consiste à passer en paramètres les objets que je dois afficher et ensuite les passer à mes fonctions libres qui s'occupent de les afficher. Comment faire pour éviter de devoir passer tous mes objets à afficher dans mes fonctions lorsqu'il y a une boucle? Une implémentation solide du MVC me permettrait-elle de résoudre mon problème? Si oui, comment faire changer ma vue si je change des données en utilisant mon controler (je suis encore dans cette boucle dont je ne peux sortir avant d'avoir fini de creuser)? Finalement, l'utilisation d'une boucle pour faire cela est-il approprié (tout simplement^^)?

    Encore beaucoup de questions, j'en suis très conscient, mais je dois dire que les réponses me sont énormément utile!
    Personnellement, je déplore que ces questions récurrentes ne fassent pas déjà partie d'une FAQ. Je ne crois pas être le seul à se poser ce genre de questions...

    Merci beaucoup,
    Sytten

    P.S.: vous pouvez prendre votre temps pour répondre, je ne suis pas pressé^^ j'ai encore quelques mois à me perfectionner avant d'entreprendre mon prochain projet et je veux être certain de le partir de la bonne façon (donc je prends le temps qu'il faut).

  6. #6
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Citation Envoyé par Sytten Voir le message
    Wow super trop top
    Enfin ça répond à beaucoup de questions sans réponse.
    Merci de ces explications très détaillées et du soin apporté à ta réponse, elle a vraiment été utile!
    Apportée avec plaisir qui plus est

    - Voir tout comme étant un service est vraiment une base qu'il me manquait. Ça démontre clairement que les setter ne sont pas une bonne chose pour l'OO...
    Certains (dont moi) estiment d'ailleurs (loi de déméter à l'appuis) que les setters sont purement et simplement aberrants
    -Exactement mon problème! Même syntaxe à quelques lettres près^^ Juste ça vient de m'ouvrir les yeux sur cette loi
    De rien, cela te permettra surement de rendre ton code beaucoup plus flexible et évolutif
    - Pour le reste de l'exemple, j'avoue que je ne dispose pas encore de toutes les connaissances nécessaires pour le comprendre (notamment au niveau des template), mais je vais certainement le relire dans un avenir rapproché (puisque je dois assimilé ces connaissances avant mon prochain projet qui sera plus ambitieux...)
    En fait, comme je te l'ai dit plus haut, les template permettent de s'intéresser uniquement à la manière dont les objets sont manipulés.

    La syntaxe est souvent verbeuse (d'où l'utilisation des nombreux typedefs pour la rendre un peu plus facile ), mais, autrement, tu dois juste te dire qu'un type générique doit "simplement" correspondre (du point de vue des services que l'on peut en attendre) à ce dont tu as besoin...

    Pour le reste, l'utilisation est fort proche de celle que l'on fait en orienté objet (ou en impératif pour les fonctions libres )

    - La constance dans les règles de nommage est bien évidemment de rigueur, j'ai lu un article l'autre jour sur les règles des devs de google (http://google-styleguide.googlecode....k/cppguide.xml). Ça peut être un début, surtout que je commence à avoir certaines règles moi-même. Il va juste être important, j'imagine, que je définisse des règles claires et précises avec ma team avant le début de notre projet...
    Lorsqu'on travaille en équipe, il est d'autant plus important d'avoir des règles de codage acceptées et utilisées par tous

    Mais l'on peut assez facilement se contenter d'un "jeu de règles de base" que l'on agrandi au fur et à mesure

    - Dans le cas des tests unitaires, il faut donc ajouter certaines lignes de code si je comprend bien le temps du test. Cela peut se faire un debug mode (afin de voir en direct la modification des données dans la fonction).
    Oui, il s'agit de rajouter du code, mais non, cela ne doit pas être directement intégré dans le code de ton projet.

    L'idée est beaucoup plus proche de celle qui consisterait à demander à un utilisateur imbécile, distrait et manquant de sommeil d'utiliser les comportements que tu as mis au point.

    Tu ne vas pas t'intéresser à la manière dont le comportement arrive à un résultat, mais au résultat lui-même, et veiller à ce que le résultat corresponde bel et bien à ce qui est attendu de la part du comportement testé.

    Généralement, on crée un (ou plusieurs) projet(s) de test en parallèle au projet proprement dit, pour éviter de devoir attendre pendant une demi heures que tous les tests soient passés avant de pouvoir tester "soi meme" le résultat, et l'on s'assure très régulièrement (et surtout après une modification / correction ou un ajout) que tous les tests continuent à se dérouler correctement.

    Question qui me vient comme ça: vaut-il la peine de prendre de le temps d'en faire un/plusieurs pour chaque nouvelle fonction? ou seulement les fonctions critiques (comprendre les plus importantes) méritent qu'on en fasse...
    Toute nouvelle fonction mérite d'être testée, soit séparément (pour les plus complexe), soit en groupe (on utilisera par exemple régulièrement "l'accesseur" sur un donnée pour vérifier que le résultat d'un comportement donné correspond à ce qu'on attendait )
    - Si on n'utilise pas ce fameux modèle MVC, y a-t-il d'autres façon de coder correctement un jeu en respectant les principes? (Afin de ne pas se retrouver avec des gods objects notamment...)
    A vrai dire, MVC rentre dans la catégorie de ce que l'on appelle les "patrons de conception" (desing patterns en anglais) tout comme le patron de conception "visiteur" ou "fabrique" ou n'importe quel autre patron décrit à l'origine par le Gof.

    Il est tout à fait possible que le simple fait de respecter certains principe (le fameux SOLID et la loi de déméter en tête) fasse "naturellement" ressembler ton projet à quelque chose de fort proche du MVC.

    La raison en est sommes toutes relativement simple : la plupart des patrons de conceptions ne sont que le résultat de la "normalisation" de pratiques que l'on rencontre naturellement dans différents projets
    - Dans le cas d'un MVC, comment les classes PersonnageControler, PersonnageView et Personnage, par exemple, doivent-elle interagir entre elles? Un lien d'amitié serait-il approprié ou doit-on passer par des fonctions membres?
    Il n'y a pas vraiment de "bonne" ni de "mauvaise" réponse à ce sujet.

    La réponse la plus adaptée ressemble beaucoup plus à "ca dépend des services que l'utilisateur est en droit d'attendre de ces différentes classes".

    Ainsi, si ton controleur (ou ta vue) doit faire appel à des services auxquels "n'importe qui" est susceptible de faire appel au niveau du modèle, la voie de la sagesse conseillerait d'utiliser les fonctions membres.

    Par contre, si ton controleur (ou ta vue) doit manipuler des éléments internes à ton modèles pour lesquels il n'y a strictement aucune raison valable pour en donner l'accès à "n'importe qui", la même sagesse conseillerait de privilégier l'amitié au fait de donner accès à qu'elle que chose qui serait estampillé "touche pas à ca p'tit con"
    - Dans un cas concret de mon jeu, j'ai une fonction dig dans laquelle il y a une boucle le temps du creusage du bloc (notamment pour ne pas que le joueur fasse d'autres actions durant le creusage). Pourtant, je dois continuer à afficher mes éléments dans cette boucle. La solution que j'ai utilisé (et que je n'aime pas puisque qu'elle est mauvaise et peu flexible) consiste à passer en paramètres les objets que je dois afficher et ensuite les passer à mes fonctions libres qui s'occupent de les afficher. Comment faire pour éviter de devoir passer tous mes objets à afficher dans mes fonctions lorsqu'il y a une boucle? Une implémentation solide du MVC me permettrait-elle de résoudre mon problème? Si oui, comment faire changer ma vue si je change des données en utilisant mon controler (je suis encore dans cette boucle dont je ne peux sortir avant d'avoir fini de creuser)? Finalement, l'utilisation d'une boucle pour faire cela est-il approprié (tout simplement^^)?
    Bon, la réponse à cette question risque d'être complexe, il va falloir t'accrocher

    Du point de vue de la conception, le fait que ton personnage est occupé à creuser mérite amplement d'être représenté au niveau de la partie métier (du modèle), tout comme le fait qu'il est en train d'attendre, de dormir, de manger, de prendre une potion, d'attaquer, de courir ou que sais-je.

    chaque "action" effectuée par ton personnage prend un "certain temps" et interdit (pour la plupart en tout cas) ton personnage de faire "autre chose" pendant qu'il est occupé à effectuer cette action.

    Une fois qu'il a fini l'action à laquelle il est occupé, il sera en mesure d'attendre l'ordre suivant qui provoquera une nouvelle action, qui prendra aussi un certain temps et qui l'empêchera de faire quoi que ce soit d'autre jusque l'action ait été effectuée entièrement, et ainsi de suite, durant toute la durée du jeu (ou peu s'en faut).

    Le fait qu'il soit occupé à attendre ou à effectuer "n'importe quelle action" sera utilisé aussi bien par le contrôleur que par la vue:

    Par le contrôleur pour savoir si ton personnage peut recevoir "l'ordre suivant", par la vue pour, le cas échéant, faire en sorte d'afficher une animation qui "mime" l'action entreprise par ton personnage.

    Le principe généralement utilisé est celui de la "machine à état": on définit une série d'états distincts ("en attente","courant", "frappant", "buvant","piochant","lançant un sort",...) qui correspond au fait que le personnage est occupé à effectuer l'action en question.

    Une fois qu'il a fini l'action qu'il est occupé à effectuer, le personnage se remet dans un état "de base" (qui est, en l'occurrence, "en attente") et qui donne accès aux autres états.

    Lorsque le contrôleur recevra l'ordre de faire effectuer une action particulière, il indiquera au modèle de de se placer dans l'état qui correspond à l'action et déclenchera un "compte à rebours" afin de pouvoir déterminer "à quel moment l'action prend fin".

    Quand le contrôleur remarquera que le compte à rebours est terminé, il indiquera au modèle qu'il est temps de remettre le personnage à son état initial ("en attente").

    Maintenant, ce n'est pas parce qu'un compte à rebours est en cours qu'il faut que le jeu se bloque et empêche le reste de fonctionner

    La solution consiste généralement à travailler avec plusieurs threads, de manière à ce que l'exécution de l'affichage de la vue (par exemple) puisse continuer de manière indépendante de ce qui se passe du coté du controleur et de manière à ce que le controleur puisse (le cas échéant) recevoir d'autres ordres pour d'autres objets alors qu'il est déjà occupé à attendre la fin d'une action pour un objet spécifique.

    Mais l'utilisation des threads mériterait à elle seul un roman, je vais donc la laisser "de coté" pour l'instant, histoire de te permettre d'assimiler tout ce que j'ai dit jusqu'à présent

    Enfin, je voudrais revenir sur un aspect qui est passé "au bleu" lors de mon intervention précédente, à savoir:
    Si on n'utilise pas ce fameux modèle MVC, y a-t-il d'autres façon de coder correctement un jeu en respectant les principes? (Afin de ne pas se retrouver avec des gods objects notamment...)
    En fait, il faut savoir que la notion de "god object" est suffisamment vague pour permettre de décrire à peu près n'importe quelle situation

    Une chose est certaine : il serait très mauvais que toutes tes classes du modèle, des controleurs et de la vue ne viennent à hériter (de manière directe ou indirecte) d'une classe Object

    Cependant, il y a parfois des situations assez confuses:

    Au niveau du modèle (de la partie métier), plus tu arriveras à garder des petites (surtout en terme de "profondeur") hiérarchies de classes, plus tu auras facile à faire évoluer ton projet et à le maintenir.

    Au niveau du contrôleur, tu auras sans doute un nombre de hiérarchie similaire à celui que tu trouve dans ton modèle, mais il n'est pas "forcément exclu" que tu puisse te retrouver avec une classe proche de AbstractControler qui serait une base commune à toutes les hiérarchies de controleurs que ton projet utilise.

    Enfin, au niveau de la vue, il semblerait assez légitimes que "tout ce qui peut être affiché" fasse partie d'une hiérarchie de classe dont la classe de base serait proche de "DrawableItem".

    Tu auras sans doute quelques (hiérarchies de) classes "périphériques", mais tout, du grain d'herbe à ton personnage en passant par les armes, les différents objets, les oiseaux qui volent dans le ciel ou les murs devront vraisemblablement pouvoir être considérés comme ... des objets susceptibles d'être tracés

    Ce sera très clairement le meilleur moyen de déterminer "quel que soit le type d'objet à tracer" les objets qu'il te faudra afficher à un instant T, et, surtout, d'être en mesure de les afficher

    Maintenant, peut on en arriver à parler de god object Il serait peut etre intéressant de lancer un débat sur le sujet car je suis relativement mitigé à ce propos
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Amélioration de code
    Par Devilju69 dans le forum Langage
    Réponses: 3
    Dernier message: 07/10/2008, 15h01
  2. Optimisations et Améliorations de code
    Par womannosky dans le forum Langage
    Réponses: 19
    Dernier message: 02/07/2008, 15h05
  3. [Optimisation]Peut-on améliorer ce code ?
    Par progfou dans le forum Algorithmes et structures de données
    Réponses: 6
    Dernier message: 09/08/2007, 15h43
  4. [Tableaux] Amélioration de code (if)
    Par Sir Tengu dans le forum Langage
    Réponses: 3
    Dernier message: 24/12/2006, 00h26
  5. [Sécurité] Comment amélioré mon code ?
    Par Nadd dans le forum Langage
    Réponses: 14
    Dernier message: 03/03/2006, 20h13

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo