Salut,
Pour chaque partie, la phrase de conclusion répond à la question, mais j'aime la justifier, alors...
POO ou Procédural?
Pour ma part, cette question requiert de répondre à une autre question: Est-ce que le jeu est focalisé sur des états ou sur des transitions? Car pour ma part, c'est là toute la différence entre l'objet et le procédural. Le premier se concentre sur la description de l'état dans lequel le jeu se trouve. En quelque sort, le jeu est "suspendu dans le temps", et on peut décrire ce qui le compose à tout instant. Le second se concentre sur les transitions: on ne sait pas dans quel état se trouve le jeu, mais ce qu'on sait, c'est qu'on peut passer de telle situation à telle autre.
Ca peut paraitre assez obscur, mais alors je vais essayer de prendre un exemple pratique, et comme un gros égoïste, je vais prendre le mien. J'ai des codes permettant de déployer mes projets et sites pour mettre à jour leur version en ligne. Pour moi, c'est un cas typique de développement procédural, car ce qui m'intéresse ce n'est pas l'état des codes du site à tout instant, mais c'est de pouvoir passer de la v1.0.0 à la v1.0.1.
Autre cas peut-être plus pertinent vu la question (centrée sur les jeux web): Eclerd. La première version (celle actuellement en ligne [lien supprimé depuis]) est codée en full procédural, car je connaissais ce PHP-là (1ere année d'école d'ingé, juste après la prépa).
Résultat? Impossible de savoir dans quel état se trouve véritablement le jeu. Je n'ai qu'un patchwork de fonctions qui essaient de passer d'un état à un autre, par exemple en simulant l'avancement du temps. Au bout du compte, c'est buggé et c'est surtout immaintenable: je ne peux plus mettre à jour mes codes car c'est tout simplement un vrac de trucs.
Pour la nouvelle version, je pars sur de l'OO. Et là, c'est bien plus agréable. J'ai un jeu stable, dans lequel les composantes sont bien délimitées et avec des objets que je peux manipuler. C'est nettement plus pratique de coder quand une usine de production est une classe que quand il s'agit d'un ramassis de variables éparses.
Du coup, je pense que tout jeu destiné à vivre un moment devrait être OO, et que le procédural peut rester dans le domaine des prototypes.
Structure: MVC ou pas?
Là, c'est peut-être une question d'expérience professionnelle, mais MVC, je peux pas le sentir.
D'une part, ce pattern est déjà implicitement inclus dans un langage OO (ben, oui, le Modèle d'une classe sont ses attributs, le Controleur est le contenu des méthodes et la View est l'API publique c'est à dire les signatures des méthodes).
Ensuite, mais c'est peut-être juste dû à la façon dont se pattern est utilisé au taff, la structure devient vite immonde. L'implémentation au boulot se résume à avoir une énorme classe "Controleur" pour chaque module. Ce controleur est alors absolument ir-réutilisable; or pourvoir réutiliser les codes est la composante essentielle d'un projet. Le Modèle se contente souvent d'être une sous-exploitation du SGBD (le Modèle est composé d'une ou plusieurs classes qui vont, pour certaines, aller chercher des données dans le SGBD via des "SELECT" et les autres essaient de manipuler ces données) et la View est un benêt template PHP qui est donc là aussi assez mal réutilisable.
Pour ma part, je n'ai donc pas appliqué ce pattern à Eclerd.
Je préfère laisse la notion de "Modèle" au SQL: la logique de jeu se trouve finalement dans le SGBD, et non dans PHP, ce qui permet d'éviter les requêtes SQL perdues tout partout dans PHP (elles sont centralisés dans des procédures SQL), d'accélérer les choses (en évitant de transférer des données vers le PHP pour les manipuler et les réinjecter dans le SQL), de mieux séparer les rôles (PHP sert à afficher le jeu, le SGBD sert à sa logique métier) et ça me parait tout simplement logique de considérer que le SGBD est un serveur "REST" comme un autre (son API publique consiste en la liste des procédures stockées).
Certes, j'ai des classes PHP qui peuvent s'assimiler au "Modèle", puisque j'ai 1 classe par appel de procédure SQL (en pratique, 1 classe par "truc logique que je veux faire", et souvent, 1 truc logique = 1 procédure SQL, mais rien ne m'interdit d'avoir 2 classes appelant la même procédure si besoin). Mais ce "modèle" ne fait pas de logique de traitement.
J'ai aussi un "simili-controleur", puisque j'ai 1 classe PHP par "truc maccroscopique à faire" ("par page" in lemon words), mais ce contrôleur sert simplement à trigger les classes de "Modèle". Ces classes ne sont d'ailleurs pas instanciées dans le contrôleur, mais dans une factory (méthode statique ou classe à part suivant le niveau de réutilisation requis).
Pour la vue et le contrôleur, le "Controleur" PHP renvoie un Bean décrivant les données de la page. Ce Bean est récupéré par un autre controleur qui le convertit en un document XML. Ce document est récupéré par un autre contrôleur qui lui applique une transformation XSL et renvoie un contenu quelconque (chaine string, le XSL pouvant avoir généré du JSON, du HTML, du XML, du PDF ou autre). Ce contenu est récupéré par un autre contrôleur qui se charge de l'envoyer au client.
Les contrôleurs sont donc imbriquées par composition façon new GZipOutput(new XSLTServerSide(new XMLizer(new MyRealControler(...))));. Cela permet d'avoir une sacrée maîtrise sur l'imbrication des contrôleurs et de réutiliser énormément de choses (le "MyRealControleur" pourrait, par exemple, servir à démolir une usine, et je pourrait le réutiliser dans un autre contrôleur, dont le rôle serait par exemple de démolir l'usine actuellement construite pour la remplacer par une autre).
Au final, le Controleur est constitué de toutes les classes PHP. La vue se trouve dans des codes XSL (mais un autre moteur de template pourrait marcher aussi). Le Modèle se trouve dans le SGBD. On retrouve plus ou moins ce MVC, mais pas celui que j'avais pu trouver sur le web (genre OpenClassRoom).
Alors, MVC oui, mais sous la forme M=MySQL, V=Moteur de template (XSL, PHP, etc) et C=Classes PHP.
UML et conception papier
La nouvelle version d'Eclerd a nécessité... 4 tentatives de redémarrage! Là, c'est ma 5e (elle me semble bien meilleure d'ailleurs). Lors des 4 premières, j'ai voulu tout planifier sur papier en amont du code, genre "pour pas me laisser piéger par une mauvaise conception". C'est une intention louable. Niveau pratique, c'est à chier.
En fait, voici ce qu'il s'est alors passé:
• Je conçois mon jeu génial pendant quelques mois, avec plein de papier
• J'étoffe le tout avec des diagrammes UML, des cas d'exemple, des analyses etc
• Je me lance dans le code...
• ...je change les plans parce que j'avais zappé un truc
• Je reprends le code
• Je recorrige la conception
• etc
• La conception ne correspond plus à rien
• Le code s'est fermé de lui-même et ne permet plus d'accueillir de nouvelles idées, ou tout simplement de suivre la conception de départ
• Le projet fait "plouf"
Pour cette 5e reprise, j'ai fait différemment:
• Je définis une fonctionnalités suffisante
• Je mets cette fonctionnalité dans un bug tracker en lui créant 1 ricket; si d'autres idées me viennent, je crée d'autres tickets
• Je code cette fonctionnalité
• Je cloture cette fonctionnalité (en commitant le code dans un logiciel de versionning, Mercurial, puis en déployant le résultat en ligne dans une zone beta et en fermant le ticket associé)
Jusqu'à présent, c'est plutôt efficace. Le concept clef est que je ne sais pas dans quelle direction ira le jeu, mais que le jeu accepte les mises à jour en étant codé de la façon la plus "élémentaire" possible: chaque classe est responsable d'une et une seule chose. Si certaines classes font plusieurs trucs, alors je considère que, de l'extérieur, elles n'en font qu'un seul. Si je veux réutiliser l'un de ces trucs par la suite, je crée de nouvelles classes, je récupère le code de la classe "qui fait trop de trucs", je l'insère dans les nouvelles classes, et la classe "qui faisait trop de trucs" se sert de ces nouvelles classes. Chaque classe est donc utile (je n'ai pas des brouettes de classes juste parce que c'est mieux de découper toujours plus son code), mais chaque classe peut évoluer si un nouveau besoin apparait.
Donc, passer des semaines/mois sur ses plans de conception, pourquoi pas, mais le jeu ne ressemblera de toute façon pas au plan. S'il y ressemble, le risque est alors qu'il ne puisse pas évoluer par la suite (et il finira par devenir désuet).
Pfiouh, ça en fait du texte
Je crois que je vais redécouper ce message plus tard pour m'en resservir dans mes articles
Pour chaque partie, la phrase de conclusion répond à la question, mais j'aime la justifier, alors...
POO ou Procédural?
Pour ma part, cette question requiert de répondre à une autre question: Est-ce que le jeu est focalisé sur des états ou sur des transitions? Car pour ma part, c'est là toute la différence entre l'objet et le procédural. Le premier se concentre sur la description de l'état dans lequel le jeu se trouve. En quelque sort, le jeu est "suspendu dans le temps", et on peut décrire ce qui le compose à tout instant. Le second se concentre sur les transitions: on ne sait pas dans quel état se trouve le jeu, mais ce qu'on sait, c'est qu'on peut passer de telle situation à telle autre.
Ca peut paraitre assez obscur, mais alors je vais essayer de prendre un exemple pratique, et comme un gros égoïste, je vais prendre le mien. J'ai des codes permettant de déployer mes projets et sites pour mettre à jour leur version en ligne. Pour moi, c'est un cas typique de développement procédural, car ce qui m'intéresse ce n'est pas l'état des codes du site à tout instant, mais c'est de pouvoir passer de la v1.0.0 à la v1.0.1.
Autre cas peut-être plus pertinent vu la question (centrée sur les jeux web): Eclerd. La première version (celle actuellement en ligne [lien supprimé depuis]) est codée en full procédural, car je connaissais ce PHP-là (1ere année d'école d'ingé, juste après la prépa).
Résultat? Impossible de savoir dans quel état se trouve véritablement le jeu. Je n'ai qu'un patchwork de fonctions qui essaient de passer d'un état à un autre, par exemple en simulant l'avancement du temps. Au bout du compte, c'est buggé et c'est surtout immaintenable: je ne peux plus mettre à jour mes codes car c'est tout simplement un vrac de trucs.
Pour la nouvelle version, je pars sur de l'OO. Et là, c'est bien plus agréable. J'ai un jeu stable, dans lequel les composantes sont bien délimitées et avec des objets que je peux manipuler. C'est nettement plus pratique de coder quand une usine de production est une classe que quand il s'agit d'un ramassis de variables éparses.
Du coup, je pense que tout jeu destiné à vivre un moment devrait être OO, et que le procédural peut rester dans le domaine des prototypes.
Structure: MVC ou pas?
Là, c'est peut-être une question d'expérience professionnelle, mais MVC, je peux pas le sentir.
D'une part, ce pattern est déjà implicitement inclus dans un langage OO (ben, oui, le Modèle d'une classe sont ses attributs, le Controleur est le contenu des méthodes et la View est l'API publique c'est à dire les signatures des méthodes).
Ensuite, mais c'est peut-être juste dû à la façon dont se pattern est utilisé au taff, la structure devient vite immonde. L'implémentation au boulot se résume à avoir une énorme classe "Controleur" pour chaque module. Ce controleur est alors absolument ir-réutilisable; or pourvoir réutiliser les codes est la composante essentielle d'un projet. Le Modèle se contente souvent d'être une sous-exploitation du SGBD (le Modèle est composé d'une ou plusieurs classes qui vont, pour certaines, aller chercher des données dans le SGBD via des "SELECT" et les autres essaient de manipuler ces données) et la View est un benêt template PHP qui est donc là aussi assez mal réutilisable.
Pour ma part, je n'ai donc pas appliqué ce pattern à Eclerd.
Je préfère laisse la notion de "Modèle" au SQL: la logique de jeu se trouve finalement dans le SGBD, et non dans PHP, ce qui permet d'éviter les requêtes SQL perdues tout partout dans PHP (elles sont centralisés dans des procédures SQL), d'accélérer les choses (en évitant de transférer des données vers le PHP pour les manipuler et les réinjecter dans le SQL), de mieux séparer les rôles (PHP sert à afficher le jeu, le SGBD sert à sa logique métier) et ça me parait tout simplement logique de considérer que le SGBD est un serveur "REST" comme un autre (son API publique consiste en la liste des procédures stockées).
Certes, j'ai des classes PHP qui peuvent s'assimiler au "Modèle", puisque j'ai 1 classe par appel de procédure SQL (en pratique, 1 classe par "truc logique que je veux faire", et souvent, 1 truc logique = 1 procédure SQL, mais rien ne m'interdit d'avoir 2 classes appelant la même procédure si besoin). Mais ce "modèle" ne fait pas de logique de traitement.
J'ai aussi un "simili-controleur", puisque j'ai 1 classe PHP par "truc maccroscopique à faire" ("par page" in lemon words), mais ce contrôleur sert simplement à trigger les classes de "Modèle". Ces classes ne sont d'ailleurs pas instanciées dans le contrôleur, mais dans une factory (méthode statique ou classe à part suivant le niveau de réutilisation requis).
Pour la vue et le contrôleur, le "Controleur" PHP renvoie un Bean décrivant les données de la page. Ce Bean est récupéré par un autre controleur qui le convertit en un document XML. Ce document est récupéré par un autre contrôleur qui lui applique une transformation XSL et renvoie un contenu quelconque (chaine string, le XSL pouvant avoir généré du JSON, du HTML, du XML, du PDF ou autre). Ce contenu est récupéré par un autre contrôleur qui se charge de l'envoyer au client.
Les contrôleurs sont donc imbriquées par composition façon new GZipOutput(new XSLTServerSide(new XMLizer(new MyRealControler(...))));. Cela permet d'avoir une sacrée maîtrise sur l'imbrication des contrôleurs et de réutiliser énormément de choses (le "MyRealControleur" pourrait, par exemple, servir à démolir une usine, et je pourrait le réutiliser dans un autre contrôleur, dont le rôle serait par exemple de démolir l'usine actuellement construite pour la remplacer par une autre).
Au final, le Controleur est constitué de toutes les classes PHP. La vue se trouve dans des codes XSL (mais un autre moteur de template pourrait marcher aussi). Le Modèle se trouve dans le SGBD. On retrouve plus ou moins ce MVC, mais pas celui que j'avais pu trouver sur le web (genre OpenClassRoom).
Alors, MVC oui, mais sous la forme M=MySQL, V=Moteur de template (XSL, PHP, etc) et C=Classes PHP.
UML et conception papier
La nouvelle version d'Eclerd a nécessité... 4 tentatives de redémarrage! Là, c'est ma 5e (elle me semble bien meilleure d'ailleurs). Lors des 4 premières, j'ai voulu tout planifier sur papier en amont du code, genre "pour pas me laisser piéger par une mauvaise conception". C'est une intention louable. Niveau pratique, c'est à chier.
En fait, voici ce qu'il s'est alors passé:
• Je conçois mon jeu génial pendant quelques mois, avec plein de papier
• J'étoffe le tout avec des diagrammes UML, des cas d'exemple, des analyses etc
• Je me lance dans le code...
• ...je change les plans parce que j'avais zappé un truc
• Je reprends le code
• Je recorrige la conception
• etc
• La conception ne correspond plus à rien
• Le code s'est fermé de lui-même et ne permet plus d'accueillir de nouvelles idées, ou tout simplement de suivre la conception de départ
• Le projet fait "plouf"
Pour cette 5e reprise, j'ai fait différemment:
• Je définis une fonctionnalités suffisante
• Je mets cette fonctionnalité dans un bug tracker en lui créant 1 ricket; si d'autres idées me viennent, je crée d'autres tickets
• Je code cette fonctionnalité
• Je cloture cette fonctionnalité (en commitant le code dans un logiciel de versionning, Mercurial, puis en déployant le résultat en ligne dans une zone beta et en fermant le ticket associé)
Jusqu'à présent, c'est plutôt efficace. Le concept clef est que je ne sais pas dans quelle direction ira le jeu, mais que le jeu accepte les mises à jour en étant codé de la façon la plus "élémentaire" possible: chaque classe est responsable d'une et une seule chose. Si certaines classes font plusieurs trucs, alors je considère que, de l'extérieur, elles n'en font qu'un seul. Si je veux réutiliser l'un de ces trucs par la suite, je crée de nouvelles classes, je récupère le code de la classe "qui fait trop de trucs", je l'insère dans les nouvelles classes, et la classe "qui faisait trop de trucs" se sert de ces nouvelles classes. Chaque classe est donc utile (je n'ai pas des brouettes de classes juste parce que c'est mieux de découper toujours plus son code), mais chaque classe peut évoluer si un nouveau besoin apparait.
Donc, passer des semaines/mois sur ses plans de conception, pourquoi pas, mais le jeu ne ressemblera de toute façon pas au plan. S'il y ressemble, le risque est alors qu'il ne puisse pas évoluer par la suite (et il finira par devenir désuet).
Pfiouh, ça en fait du texte
Je crois que je vais redécouper ce message plus tard pour m'en resservir dans mes articles