samedi 25 novembre 2017

Chute libre dans le vide sous Processing

Simuler une phénomène naturel en utilisantl'animation est un moyen excellent pour l'enseigner et pour l'expliquer. Cette approche permet, aussi, de modifier les différents paramètres et voir les différents scénarios possibles avec un coût (pratiquement) nul.
Dans cet exemple, nous allons simuler une chute libre dans le vide. Pour rendre la simualtion un peu plus réel, nous allons ajouter un effet de rebond par définir un facteur de préservation de l'énergie au moement de l'impact au sol.
L'objet en question est une boule. Elle sera définie comme un objet séparé pour donner la possibilité de comparer plsuieurs chutes avec des paramètres différents.
Le problème peut être modélisé suivant deux approches :
  • L'utilisation des équations en fontion du temps "t", la position et la vitesse de l'objet seront calculées en fontion de l'instant "t" de l'animation.
  • En utilisant des suites pour la vitesse te la position. Les paramètres de l'instant "t" seront obtenus en fontion des paramètres de l'instant "t - 1".
Dans cet exemple, nousa allons suivre cette deuxième approche. Le passage d'un instant à l'instant suivant est implémenté sous la forme d'une fonction que nous appelerons chaque intervalle du temps (un autre paramètre que nous pouvons modifier à notre guise).

 


Le code principal est très simple. Il initialise l'environnement et déclare une boule. Chaque fois, il dessine la boule avec sa position, sa vitesse et son énergie.


Boule b;

void setup(){
  size(600, 400);
  b = new Boule(100, 0, 50, 10, 0.9f);
}

void draw(){
  background(255);
  b.draw();
  if(frameCount % 6 == 0)
    b.instantSuivant();
  fill(0);
  text("" + b.getVitesse(), 10, 30);
  text("" + b.getEnergie(), 10, 50);
  text("" + b.getY(), 10, 70);
};

Le code  de l'objet Boule permet de garder les différentes information de la boule, de simuler la chute et de déssiner la boule :


class Boule {
  
  public final static float G = 1f;
  
  float preservation;
  int x, y, diametre, vitesse, masse, energie;
  
  public Boule(int x, int y, int diametre, int masse, float preservation){
    this.x = x;
    this.y = y;
    this.diametre = diametre;
    this.masse = masse;
    this.preservation = preservation;
  }
  
  public void instantSuivant(){
    y += vitesse;
    energie = masse * vitesse * vitesse / 2;
    if(y >= height - diametre / 2){
      vitesse = (int)(-1 * preservation * Math.sqrt(2 * energie / masse));
    }else{
      vitesse += G;
    }
  }
  
  public void draw(){
    fill(255);
    stroke(0);
    ellipse(x, y, diametre, diametre);
  }
  
  public int getVitesse(){
    return vitesse;
  }
  
  public int getEnergie(){
    return energie;
  }
  
  public int getY(){
    return y;
  }
  
  public int getX(){
    return x;
  }
    
  
}

La fonction qui nous intéresse le plus est la fonction "instantSuivant()". Elle montre le changement appliqué sur la vitesse :


  public void instantSuivant(){
    y += vitesse;
    energie = masse * vitesse * vitesse / 2;
    if(y >= height - diametre / 2){
      vitesse = (int)(-1 * preservation * Math.sqrt(2 * energie / masse));
    }else{
      vitesse += G;
    }
  }

  • Si l'objet est en chute, la vitesse est influencée par la gravité seulement.
  • Si la boule touche le sol, la gravité n'est plus appliquée mais c'est le rebond qui repousse la boule vers le haut.
 
L'état final de l'animation est la boule immobile au fon d de l'écran (le sol).
Nous pouvons facilement ajouter une autre boule pour comparer les deux chutes :


Boule b1, b2;

void setup(){
  size(600, 400);
  b1 = new Boule(100, 0, 50, 10, 0.9f);
  b2 = new Boule(200, 0, 80, 10, 0.5f);
}

void draw(){
  background(255);
  b1.draw();
  b2.draw();
  if(frameCount % 6 == 0){
    b1.instantSuivant();
    b2.instantSuivant();
  }
  fill(0);
  text("b1", 10, 10);
  text("" + b1.getVitesse(), 10, 30);
  text("" + b1.getEnergie(), 10, 50);
  text("" + b1.getY(), 10, 70);
  text("b2", 10, 90);
  text("" + b2.getVitesse(), 10, 110);
  text("" + b2.getEnergie(), 10, 130);
  text("" + b2.getY(), 10, 150);
};


mercredi 8 novembre 2017

Bases de l'animation sous Processing (partie 2)


Dans la première partie, nous avons préciser les éléments les plus basiques pour pouvoir dessiner sous Processing. Animer n'est pas très différents du dessin, surtout si nous avons pas mal de notions déjà implémentées.
Animer c'est dessiner plusieurs images légèrement différentes d'une  manière successive pour créer l'illusion qu'il s'agit d'un objet en animation. La fréquence de l'animation et le degré de changement entre les images jouent un rôle important dans la qualité de l'animation.
D'un côté, si la fréquence de dessin est top basse, l'animation est perdue parce qu'elle devient un ensemble d'états (de l'objet en question) clairement distincts. Si elle est trop élevé, l'animation est perdues parce que l'utilisateur ne peut voir que le premier et le dernier états; il aura la sensation que l'objet en question a été téléporté de la première position vers la deuxième position.
De l'autre côté, les différences entre les différentes images doivent être non null, autrement il n'y a plus d'animation mais des objets immobiles. Comme elles ne doivent pas être trop grandes, sinon, on risque d'avoir le même effet de téléportation sensé avec une grande fréquence de dessin ( de mise à jour).
De toute ces notions, Processing a essayé d'implémenter une bonne partie. Si les objets de l'animation et la différence entre les différentes images (frames) sont la responsabilité de l'utilisateur (chaque utilisateur peut créer ce qu'il veut), Processing a pu apporté sa valeur ajoutée concernant le principe de l'animation et de la fréquence de l'animation.
Un script de base d'animation sous Processing doit définir les deux fonctions : setup() et draw().
setup()  : est, comme son nom l'indique, une fonction d'initialisation qui sera appelée une seule fois avant toutes les autres fonctions. Cette fonction est généralement utilisée pour initialiser les variables manipulées et les définir les propriétés générale de l'animation telles que l'arrière plan et la taille de la fenêtre.
draw() : est la fonction qui permet de dessiner le contenu de la fenêtre. Cette fonction est appelée 60 fois par seconde. Cette philosophie permet d'éviter au utilisateur de gérer deux choses :
  1. La boucle de dessin,
  2. Le rythme de dessin.
Prenons un exemple :


int i, j;

void setup(){
  size(600, 600);
  i = 0;
  j = 0;
}

void draw(){
  i++;
  j++;
  ellipse(i, j, 5, 5); // un cercle de 5 pixels
}



Ce script nous permet de dessiner une ligne qui grandit peu à peu en diagonale de la fenêtre. La question ici est : comment avons nous obtenu une ligne malgré que le script fait appel à la fonction "ellipse()" qui dessine un seul point ?
La réponse réside dans le fait que Processing ne vide pas la fenêtre entre deux appels à la fonction "draw()"; il y ajoute les nouvelles formes sans effacer les anciennes.
La méthode la plus simple pour effacer le contenu de la fenêtre est de re-dessiner l'arrière plan à chaque appel de la fonction "draw()". Ainsi, le script peut être modifié pour avoir un point qui se déplace en diagonale de la fenêtre au lieu d'avoir une ligne qui grandit.


int i, j;

void setup(){
  size(600, 600);
  i = 0;
  j = 0;
}

void draw(){
  background(255);
  i++;
  j++;
  ellipse(i, j, 5, 5);
}

A la fin, le point en déplacement finit par sortir de la fenêtre.


Le règles de dessin reste les mêmes que les règles présentées dans la première partie de cet article.

Note : l'utilisation de la méthode "ellipse()" qui dessine, à la base, la forme ovale nous permet de dessiner une un grand point. La méthode "point()" permet de dessiner un seul point mais il ne sera pas très visible sur les captures.

Bases de l'animation sous Processing (partie 1)

Processing facilite la création des animation. Il implémente les notions de base de toute animation. Dans cet article (sur deux parties), je vais brièvement présenter la différence entre un script de dessin et un script d'animation sous Processing.
Pour dessiner une forme, aucune condition préalable est nécessaire sauf la taille de la fenêtre. Cette taille peut être définie par la fonction "size". Elle respecte la norme en prenant le premier paramètre comme la largeur de la fenêtre et le deuxième paramètre comme sa hauteur. Par exemple :


size(600, 400);

Nous donne la fenêtre suivante :


Et puisque Processing cache la complexité liée à la programmation Orientée Objet en Java, la ligne de code précédente est tout ce que nous allons mettre dans le script.


Pour dessiner une forme donnée, il faut appeler la fonction adéquate. Une ligne est dessinée grâce à la fonction "line()" et une rectangle est dessiné en appelant la méthode "rect()". Chaque fonction nécessite des paramètres selon la forme que nous voulons dessiner. Par exemple, le code suivant :

size(600, 400);
rect(100, 100, 400, 200);

Nous donne la forme suivante :


Nous pouvons appeler plusieurs fonctions de dessin comme toutes les autres fonctions. Par exemple, le code suivant :

size(600, 400);
rect(100, 100, 400, 200);
line(100, 100, 500, 300);
line(100, 300, 500, 100);

Nous donne la forme suivante :


Il est possible de modifier l'ensemble des paramètres des dessin par appeler les fonctions adéquates avant d'appeler la fonction de dessin. Parmi ces paramètres, nous pouvons préciser la présence ou bien l'absence d'une ligne qui délimite la forme, la couleur de cette ligne et la couleur de remplissage. Par exemple, dans ce code :


size(600, 400);
noStroke();
fill(0, 200, 130);
rect(100, 100, 400, 200);

Nous avons demandé à Processing de ne pas dessiner une ligne autour de notre rectangle comme nous avons demandé que le remplissage aura la couleur 0 pour le rouge, 200 pour le vert et 130 pour bleu (un codage de 0 à 255 pour chaque couleur, d'autres modes existent sous Processing). Le résultat est :


Cela est concernant le dessin, comment créer des animations ? Voir partie 2.

Le trésor caché

L'évolution rapide des différentes technologies liées à la programmation et au développement complique de lus en plus la tâche du développeur. Être un développeur d'une technologie particulière veut dire suivre ses différentes avancées et faire la mise à jour de ses connaissances pour pouvoir exploiter et bénéficier des nouvelles versions et des nouvelles frameworks. Cette tâche devient très difficile vu le rythme suivant lequel les nouvelles propositions sont déployées. Ainsi, le développeur se trouve face à deux solutions.
La première solution est la spécialisation dans un ensemble donné de technologies. A mon avis, c'est la solution suivie par la plus part des développeurs. Chaque développeur choisit la technologie qu'il trouve meilleure pour lui (comme préférence personnelle) ou bien pour son travail (exigence professionnelle). Après plusieurs années d'expérience, il sera un expert dans ces technologies. La plus part des auteurs des livres font partie de cette catégorie.
La deuxième solution est l'apprentissage limité selon le besoin. Dans le cas où le développeur se trouve obligé de travailler sur plusieurs projets avec des technologies différentes, il peut apprendre une partie ou une API d'un langage ou d'une plate-forme donné suffisamment pour pouvoir satisfaire son engagement professionnel mais pas pour mériter le titre d'expert ou pour occuper le poste de consultant dans cette technologie. Je ferai beaucoup moins confiance dans un livre si je trouve que son auteur soit de cette catégorie.
Dans les deux cas, il restera probablement des trésors cachés. Et par trésor, je veux dire un outil, un langage, une API ou une extension que vous ne connaissez même pas l'extasient ou que vous n'utilisez pas son potentiel d'une manière correcte.
C'est mon cas avec le langage Processing. Processing : "(autrefois typographié Proce55ing) est une bibliothèque Java et un environnement de développement libre (sous licence GNU GPL), créé par Benjamin Fry et Casey Reas, deux artistes américains. Processing est le prolongement « multimédia » de Design by numbers, l'environnement de programmation graphique développé par John Maeda au Media Lab du Massachusetts Institute of Technology" (Wikipedia, simplement). Processing cache une grande partie de la complexité relative à la programmation Orientée Objet que nous trouvons dans Java. Il permet aussi de créer facilement des graphiques et des animations en implantant une grande partie des notions relatives à l'animation; la chose qui est complètement absente dans Java.
Dans mon cas, j'ai toujours utilisé les Applets pour implanter des animations de base à des fin pédagogiques et d'enseignement. Avec l'abondant des Applets, j'ai cherché une solution alternative et j'ai tombé sur Processing. Ma première impression était un regret profond de toutes les années que j'ai perdues en utilisant les Applets au moment où je pouvait faire appel à Processing. En effet, pour dessiner un carré, il vous suffit 2 lignes de code :

size(600, 400);
rect(100, 100, 400, 200);



Processing est le plus grand trésor sur lequel j'ai tombé cette année. Avec les possibilités qu'il offre (quelques exemples), il fera, probablement, partie de mon quotidien durant les prochains jours où  je tenterai de l'explorer et de l'exploiter.
Note Importante : Apprendre une technologie ou un langage ne se résume pas dans l'apprentissage du syntaxe de base ou bien de l'API de base seulement. Le développeur doit avoir des connaissances plus approfondie et doit pouvoir faire face aux problèmes et erreurs souvent rencontrés ou même non-documentées.

mardi 14 février 2017

Ecrire des outils personnels, pourquoi est-ce important ?

Il est possible, aujourd'hui, de trouver toute sorte de logiciel sur le marché. De plus petit utilitaire au plus grand logiciel, les offres se multiplient, soit pour les logiciels payants, soit pour les logiciels Open Source. Il est même possible de faire appel aux outils en ligne, suivant (presque) la philosophie de SAAS. Ainsi, il est possible d'utiliser des outils pour dessiner des graphes, prendre des notes ou même écrire et compiler des programmes sans rien installer sur la machine.
Néanmoins, nous nous trouvons des fois face à des problématiques particulières, ou bien, face à des exigences particulières imposées par notre propre environnement de travail et la combinaison unique des logiciels installés et leur utilisation dans le cadre de notre propre activité quotidienne. Cette combinaison est, à mon avis, unique et c'est pourquoi le phénomène "Work on my machine" est toujours présent entre les développeurs.
Cet environnement unique nécessite, par conséquent, une configuration unique. Il faut adapter les différents outils et logiciels ainsi que le système d'exploitation installé pour répondre à nos exigences particulières. Ainsi, nous nous trouvons entrain de lire les différentes ressources de documentation, de visiter les sites dédiés des logiciels installer et sauter entre les différents dossiers pour modifier tel ou tel fichier en ajoutant des commandes par ci et en "dé-commentant" une ligne par là.
Une situation plus délicate est le passage d'un système à un autre; dans l'état actuel des choses, ces actions de configuration manuelle sont (pratiquement) impossibles à transmettre entre l'ancienne version et la nouvelle version (ou l'ancien environnement et le nouvel environnement). Ma récente expérience avec une mise à niveau majeure de mon système m'a fait vivre cette situation avec tous ses détails : j'étais obligé de reprendre l'installation de mes outils à zéro (chose qui n'est pas vraiment pénible), en plus, il fallait les configurer. J'étais, à titre d'exemple, surpris de voir mon installation locale de la plateforme Moodle s'arrêter malgré le respect des différentes consignes et après plusieurs heures de vérification j'ai trouvé que la cause est le manque d'une extension PHP qui n'est plus installée avec l'installation par défaut.
La question qui se pose est : pourquoi dépendre de tous ces outils "prêt-à-porter" et leur pénible configuration ? Une autre solution que je propose est le développement de nos propres outils lorsqu'il s'agit des petits utilitaires ou bien des logiciels que nous n'utilisons qu'une faible partie de leurs fonctionnalités (nous n'utilisons, en général, que 10% des fonctionnalités des logiciels installés sur nos machines). L'idée de ce blog m'est venue en lisant, depuis quelques minutes, une question sur un forum où son auteur se demandait est ce que nous devéloppons nos propres logiciels. La liste des réponses étaient longue : des logiciels de gestion des dépenses, d'autres pour gérer l'emploi du temps des enfants, des outils pour convertir entre différents formats, et plein d'autres outils que les développeurs développent pour eux-même et pour faciliter leurs activités quotidiennes.
A mon avis, avoir un ensemble des outils personnels est une nécessité pour chacun d'entre nous. Avoir le courage de développer les fonctionnalités les plus utilisées dans un logiciel pour ne plus dépendre de ce dernier me semble très justifié quoique la tradition soit de ne plus réinventer la roue. Mais est-ce cette roue est vraiment utile pour moi ? Et si c'est le cas, pourquoi je me trouve obligé de la modifier pour l'agrandir par ci et la réduire par là ?
Durant ma dernière mise à niveau, l'outil que j'étais sûr qu'il fonctionnerait était un simple outil que je développais pour les séances de TP ou j'étais obligé de créer rapidement des classes et des scripts SQL; l'outil me permet de créer les deux en exécutant une seule (mais longue) commande. Pourquoi être sûre ? Parce qu'il s'agit de mon outil, je connais tous ses détails et je peux me retrouver facilement entre les lignes de son code source et de sa documentation. Je n'ai plus besoin de faire des recherches, de poser des questions sur les forums ou bien de contacter son support technique. Et c'est exactement là que réside la puissance des outils personnels.

jeudi 26 janvier 2017

Chargement dynamique des classes : le fondement des systèmes basés sur les plugins

Les systèmes basés sur le principe des plugins sont facilement extensibles. Malgré cela, il est rare de faire appel à cette approche dans le contexte des entreprises (sauf l'utilisation des plateformes très stable comme NetBeans Framework); les dépendances sont très faibles entre les composants et aucune vérification n'est pas possible avant l'exécution réelle du système. Dans un autre contexte, cette approche peut être vue comme la solution idéale pour concevoir un système.
Parmi ces contextes, nous trouvons le développement des outils. Un outil est censé offrir un ensemble de services ou de fonctions pour accomplir une tâche donnée mais sans faire partie du noyau du système qui doit rester toujours stable. Cette approche permettra d'ajouter un aspect dynamique pour l'extension et pour la configuration des outils développés.
Un autre contexte est le contexte de la recherche. Il n'est pas rare de se trouver dans la situation de test et d'évaluation de plusieurs approches, techniques ou algorithmes qui "font la même chose" mais avec des méthodes différentes. Avoir la possibilité de charger automatiquement, pendant l'exécution ou même pour la simple raison de ne pas toucher au noyau peut être très utile dans ce contexte.
La manipulation des classes et l'appellation des méthodes d'une méthode dynamique sont très courantes en Java. D'ailleurs, elles représentent le fondement des CDI (Context and Dependencies Injection) qui ont rendu la J2EE la plateforme la plus légère pour le développement entreprise.
La rubrique de base est la classe "Class" qui permet la manipulation des classes. Dans cet exemple, nous allons charger dynamiquement une classe en se basant sur son nom. Le nom doit être le nom complet comme s'il s'agit d'une instruction "import", le fichier jar qui contient la classe ne doit pas forcément être le jar du système, il suffit qu'il soit accessible (inclus dans la classpath) :

Class classeChargee = Class.forName(nom_de_la_classe);
Pour créer une instance, une méthode est déjà prête :

MaClasse operation = (MaClasse) classeChargee.newInstance();
Dans notre exemple, le service requis est le clacul de la somme de deux entiers (le service le plus simple au monde). Pour le définir, nous utilisons une interface (c'est la même idée utilisée dans les services web) :

public interface Somme {
    
    /**
     * Pour simplifier, il s'agit du calcul d'une somme.
     * @param a Le premier opérand.
     * @param b Le deuxième opérand.
     * @return  La somme des deux opérands
     */
    public int somme(int a, int b);
    
}
Pour voir au mieux l'avantage de la technique présentée, nous allons définir deux classes qui implémentent le service Somme : Somme1 et Somme2.

public class Somme1 implements Somme{

    @Override
    public int somme(int a, int b) {
        System.out.println("Calcul en utilisant Somme1");
        return a + b;
    }
    
}


public class Somme2 implements Somme{

    @Override
    public int somme(int a, int b) {
        System.out.println("Calcul en utilisant Somme2");
        return a + b;
    }
    
}
Pour simplifier le code, nous allons donner les noms des classes dynamiquement par saisie. Il est possible de scanner tel ou tel dossier pour récupérer la liste des fichiers (c'est pour cela qu'il faut mettre les plugins dans le même dossier) ou bien de mettre la liste des classes dans un fichier texte. La méthode main sera ainsi :

public static void main(String[] args) {

        if (args.length != 2) {
            // Nous attendons deux nombres entiers pour calculer la somme.
            System.out.println("Usage CalculerSomme nbr1 nbr 2\nExemple : CalculerSomme 12 25");
        } else {

            int a = Integer.parseInt(args[0]);
            int b = Integer.parseInt(args[1]);
            
            Scanner entree = new Scanner(System.in);
            String laClasse;
            while (true) {

                laClasse = entree.nextLine();

                try {
                    Class classeChargee = Class.forName(laClasse);
                    Somme operation = (Somme) classeChargee.newInstance();

                    System.out.println("La somme de a et b est : " + operation.somme(a, b));
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException ex) {
                    ex.printStackTrace();
                }

            }

        }
    }
L'exécution montre le résultat de la saisie du nom de la classe Somme1 ensuite le nom de la classe Somme2 :

Le code source est téléchargeable par ici.

vendredi 20 mai 2016

Voilà comment "un cours" devient "une chaîne de caractères"

La conception et la réalisation d'un système quelconque nous pousse à travailler, dans la plus part des cas, avec des niveaux d'abstraction différents. L'idée est de commencer par un niveau élevé d'abstraction et de descendre, étape par étape, vers le niveau physique qui est l'implémentation de la solution. Ce passage n'est pas souple, ni sans risque; une chose qui m'a pris deux années et deux projets pour la réaliser.
Les niveaux d'abstraction sont utilisés depuis les premières approches d'analyse et de conception des systèmes informatiques et des systèmes d'information. Merise, l'une des premières approches, définit trois niveaux d'abstraction :
  • Le niveau conceptuel : le niveau d'abstraction le plus élevé. A ce niveau, on est supposé donner une première modélisation du système en essayant de répondre à la question "Quoi" pour les deux dimensions statique (données) et dynamique (traitements) du système.
  • Le niveau logique (organisationnel) : le deuxième niveau d'abstraction. A ce niveau, on commence à détailler la modélisation donnée au niveau conceptuel en ajoutant les réponses aux questions "Qui" et "Quand".
  • Le niveau physique : le niveau d'abstraction le plus bas; on parle de l'implémentation physique. A ce niveau, la conception (la modélisation) détaillée donnée au niveau logique est traduite à un ensemble de programmes, logiciels, fichiers et bases des données.
Cette définition (très) simplifiée nous montre qu'il est possible de faire une conception et une réalisation souples avec des passages claires entre les différentes étapes. Malheureusement, deux projets durant les deux dernières années ont prouvé que, sous certaines circonstances, c'est pas aussi évident que cela.
En travaillant sur deux projets en e-Learning (comme toujours), les étudiants impliqués ont montré le même comportement malgré que les deux projets étaient complètement indépendants. Le comportement peut être résumé par un constat que me frappait il y a environ deux semaines en essayant d'expliquer à un collègue ce qui ne marche pour le projet en cours : "le cours de l'enseignant est, pour eux, une chaîne de caractère".
Sur ce point, vous allez me dire que c'est normal : toute donnée sera, quelque part durant le processus, traduite vers une donnée binaire pour être implémentée sur la machine. Ainsi, le solde bancaire d'un employé n'est qu'un nombre réel, la valeur d'un crédit qui peut contrôler la vie d'une famille n'est qu'un nombre réel aussi, un avion civil avec des centaines de passagers n'est qu'un cercle de quelques pixels sur un écran et même une déclaration de guerre entre deux pays n'est qu'une chaîne de caractères. Tout cela semble être vrai mais il n'est pas du tout.
Comme étant des humains, nous essayons toujours de donner plus de sens à des évènements qui semblent beaucoup plus simples que ceux cités ci-dessus. Nous tentons, aussi, de détecter des modèles, des lois, des principes et de proposer des théories pour les phénomènes qui nous entourent. En ajoutant ces capacités à nos capacités de manipulation des symboles, nous nous trouvons face à l'intelligence humaine (encore une fois, les définitions sont simplifiées : chaque définition peut nous conduire à écrire un livre entier). Ainsi, au moment où l'enseignant voit son cours comme le fruit d'un effort particulier d'analyse, de résumé et de synthèse, le développeur le voit comme une chaîne de caractères à insérer dans une base des données et à le récupérer plus tard. Ce décalage me semble anormal pour ne pas dire inquiétant pour le projet.
La cause principal, à mon avis, est la mauvaise division des tâches entre les membres de l'équipe, et plus particulièrement, les division de type : "tu fais l'état de l'art et je me charge de ma programmation". Une telle division semble marcher : le résultat finale de la première moitié est un ensemble de schéma descriptifs qui expliquent au moindre détail les composants du système, les traitements à effectuer pour chaque composant et les données à manipuler par chaque traitement. La deuxième moitié du travail consiste à reprendre cette description et à la traduire vers une technologie (niveau physique) pour terminer le projet. Et c'est ce passage qui n'est pas évident : sans suffisamment d'information sur le domaine, les préférences des clients et la nature des besoins, la conception détaillée sera vue par le développeur vide, sans âme et même sans importance particulière. C'est à ce niveau que le cours d'un enseignant et l'adresse d'un apprenant deviennent de la même importance, très similaires et
implémentées de la même façon : une chaîne de caractère.

Un dernier mot

Pour cet article, j'ai fait deux grandes simplifications (ce n'est pas sans risques) en essayant de comprendre ce phénomène qui pousse les étudiants (ou les employés) à vider un projet donné de son sens. L'objectif visé n'est pas de prouver ses origines parce que je ne les connais pas, c'est ma première réflexion sur le sujet. Ce que j'espère est d'exposer ce risque au sein des équipes de développement.