Outils d'utilisateurs

Outils du Site


conception

Différences

Cette page vous donne les différences entre la révision choisie et la version actuelle de la page.

Lien vers cette vue

conception [2017/04/18 01:43]
gbdivers
conception [2017/04/26 04:11] (Version actuelle)
gbdivers
Ligne 194: Ligne 194:
 La réutilisabilité du code est quelque chose que vous utiliser depuis le début de ce cours, sans forcément le réaliser. Ce terme signifie simplement que le même code est //peut être// réutiliser plusieurs fois. Par exemple, quand vous utilisez des fonctionnalités de la bibliothèque standard, vous réutilisez le même code (écrit par quelqu'un d'autre que vous) plusieurs fois.  La réutilisabilité du code est quelque chose que vous utiliser depuis le début de ce cours, sans forcément le réaliser. Ce terme signifie simplement que le même code est //peut être// réutiliser plusieurs fois. Par exemple, quand vous utilisez des fonctionnalités de la bibliothèque standard, vous réutilisez le même code (écrit par quelqu'un d'autre que vous) plusieurs fois. 
  
-Il faut bien comprendre qu'avoir un code réutilisable est différent de réutiliser un code. Un code réutilisable est un code //conçu// pour pouvoir être réutiliser plusieurs fois (qu'il soit effectivement réutiliser ou non). Si vous concevez un code non réutilisable, vous avez toutes les chances qu'il ne puisse pas être réutilisé. Ecrire un code réutilisable demande un peu plus de travail, mais le gain de temps sur le long terme est plus intéressant. Vous ne pouvez pas savoir l'avance que vous n'aurez pas besoin de réutiliser un code.+Il faut bien comprendre qu'avoir un code réutilisable est différent de réutiliser un code. Un code réutilisable est un code //conçu// pour pouvoir être réutiliser plusieurs fois (qu'il soit effectivement réutiliser ou non). Si vous concevez un code non réutilisable, vous avez toutes les chances qu'il ne puisse pas être réutilisé. Ecrire un code réutilisable demande un peu plus de travail, mais le gain de temps sur le long terme est plus intéressant. Vous ne pouvez pas savoir à l'avance que vous n'aurez pas besoin de réutiliser un code.
  
 <note> <note>
Ligne 202: Ligne 202:
 </note> </note>
  
 +==== Le copier-coller ====
  
 +Pour savoir si un code est réutilisable, il faut se poser la question de comment il pourra être réutilisé et quels seront les freins à sa réutilisation.
  
 +Une approche "historique" est de simplement copier-coller un bout de code ("snippet") lorsque vous en avez besoin. Certains éditeurs de code proposent même des fonctionnalités pour réaliser automatiquement cette tâche. C'est particulièrement utile par exemple pour ajouter la licence logicielle en en-tête des fichiers, la documentation technique du code ou un modèle de code pour une classe.
  
 +L'inconvénient de cette approche est qu'ensuite les modifications doivent être fait pour chaque bout de code que vous avez coller. Par exemple, si vous écrivez une boucle ''for'' avec des itérateurs pour parcourir une collection et que vous copiez ce code a une centaine d'autres endroits dans votre code. Puis, quelques mois plus tard, vous changez d'avis et voulez remplacer cette boucle ''for'' par une boucle ''range-for''. Comment faire ca simplement ? C'est assez difficile : il faut retrouver tous les endroits ou vous avez copier votre code, puis le corriger. Vous risquez donc d'oublier des endroits ou de vous trompez en corrigeant le code.
  
-  * copier-coller : probleme de maintenance +Le copie-coller de code est donc possible pour le code non critique (commentaires, documentation, etc) mais est à éviter dans les autres cas. 
-  * est-il facile de reutiliser un code + 
-  * blocage a la réutilistion : non generiqueon sait pas qu'un code existe dejaon ne sait pas comment réutiliser un codeetc+==== Les macros ==== 
 + 
 +Un moyen d'éviter le problème de maintenance d'un code que vous copiez-collez est d'avoir une seule version d'un code et de faire appelle a celui-ci quand vous en avez besoin. 
 + 
 +Une méthode historique simple est de définir une macro qui contient votre code, puis d'appeler plusieurs fois cette macro. (Cette approche est en particulier utilisée en C pour écrire du code générique). 
 + 
 +<code cpp> 
 +#define print(x) std::cout << x << std::endl 
 + 
 +int main() { 
 +    print(123); 
 +    print("hello"); 
 +
 +</code> 
 + 
 +Les macros permettent certaines fonctionnalites specifiques, donc il est parfois intéressant de les utiliser. Mais elles presentent egalement de gros problèmes (en particulier que c'est du remplacement de chaînes sans tenir compte du contexte et qu'il n'y a pas de vérification des types de paramètres). Il est donc préférable de limiter leur utilisation et préférer les fonctions (et les templates pour le code générique). 
 + 
 +==== Les fonctions ==== 
 + 
 +Les fonctions sont la base de la réutilisabilité en programmation procédurale. C'est même pour cela que les fonctions ont ete initialement creees. 
 + 
 +Vous connaissez déjà les fonctions, mais voici un petit rappel de ce qu'elles apportent en termes de réutilisabilité : 
 + 
 +  * isoler un traitement de données. Une fonction doit faire une chose et le faire correctement (principe de responsabilité unique). 
 +  * documenter un traitement. Le nom de la fonction et des paramètres doivent permettre aux utilisateurs de savoir ce que fait une fonction et comment l'utiliser. Si nécessaireil est possible de compléter en ajoutant des commentaires et de la documentation. 
 +  * imposer une interface. Une fonction prend un certain nombre de paramètres, ayant des types définis. L'utilisateur doit respecter la signature de la fonction, sous peine que le compilateur rejette le code. Une bonne interface est une interface qui facilite l'utilisation correcte de la fonction et décourage les utilisations incorrectes. 
 +  * écrivez du code générique. Plus un code sera generiqueplus il sera utilisable dans de nombreuses situations. Par exemple, si vous écrivez une boucle ''for'' utilisant l'opérateur ''[]'', votre code ne sera utilisable qu'avec des tableaux, pas toutes les collections. Avec des itérateurs ou une boucle ''range-for'', n'importe quelle collection sera utilisable. N'ajoutez pas de contraintes si elles n'ont pas lieu d'être. 
 + 
 +Les classes (qui seront détaillées dans la partie Programmation Orientée Objet) est une extension de cette approche. Au lieu de simplement isoler un traitement de données dans des fonctionsles classes permettent d'isoler des données et leurs traitements associées, pour former un tout cohérent (et donc plus simple à réutiliser).
  
 Plus généralement, créer une bibliothèque logicielle permet de faciliter la réutilisation du code. C'est pour cela qu'une partie importante de la conception se focalise sur la création de bibliothèques logicielles, une application n'étant au final qu'un ensemble de bibliothèques qui interagissent entre elles. Plus généralement, créer une bibliothèque logicielle permet de faciliter la réutilisation du code. C'est pour cela qu'une partie importante de la conception se focalise sur la création de bibliothèques logicielles, une application n'étant au final qu'un ensemble de bibliothèques qui interagissent entre elles.
 +
 +==== Pourquoi un code réutilisable n'est pas réutilisé ? ====
 +
 +Malgré le soin que vous pouvez apporter pour que votre code soit le plus réutilisable possible, il peut arriver que votre code ne soit en pratique pas (ou peu) réutilisé. Essayer de comprendre pourquoi les utilisateurs ne réutilisent pas votre code peut aider à améliorer sa réutilisabilité.
 +
 +Le premier blocage peut être simplement que les utilisateurs ne savent pas que votre code existe. Si vous travaillez sur un projet seul, pendant "que" quelques semaines, et qui contient "que" quelques milliers de ligne de code, il sera possible de ne pas oublier qu'un code existe déjà. Sur un projet en équipe, sur plusieurs années, avec plusieurs millions de ligne de code, il sera facile de ne pas savoir qu'un code existe déjà.
 +
 +Pour éviter cela, plusieurs pistes sont possibles :
 +
 +  * organisez correctement vos projets. Décomposez en modules facilement identifiables, créez des répertoires pour ranger vos fichiers (l'organisation en modules et répertoires sera détaillé dans un prochain chapitre).
 +  * écrivez de la documentation techniques, expliquant l'organisation du projet, les informations importantes à connaître, comment vous avez conçus le code. (Plus les utilisateurs comprendront comment vous avez pensé votre code, plus ils arriveront à trouver facilement les informations dont ils ont besoin).
 +  * transmettez vos connaissances et faites en sorte que les autres développeurs partagent aussi leurs connaissances. Une technique classique pour cela est de faire du //peer-reviewing//, qui consiste à se relire mutuellement le code entre développeurs. (Cela sera détaillé dans le chapitre sur la gestion de projet).
 +
 +Pour résumer, la communication entre développeurs est la base de la reutilisabilite. N'attendez pas simplement que les développeurs communiquent, mettez en place à l'avance les outils et méthodes facilitant la communication.
 +
 +En particulier, réévaluer constamment les outils et méthodes. Demandez vous si les méthodes sont bien comprises et bien appliquées. Si les outils sont efficaces. Si les besoins ne changent pas. Soyez "Agile". (La mthode de gestion de projet "Agile" sera également développé dans un prochain chapitre).
  
  
 ===== La testabilité ===== ===== La testabilité =====
  
-Pareil, c'est une propriété du code, testabilité !tester un code+La testabilité d'un code correspond a la facilite ou non de tester un code. Comme pour la réutilisabilitéil faut distinguer un code testable et un code testée. La testabilité est une propriété intrinsèque d'un code, qu'il soit effectivement testé ou non. 
 + 
 +==== Tests automatiques et manuels ==== 
 + 
 +Les tests ont une importance particulière dans certaines méthodes de gestion de projets, en particulier dans les méthodes Agile, qui sont utilisées dans ce cours. Plus spécifiquement, vous verrez dans les prochains chapitres le développement dirigé par les tests (TDD, pour //Tests Driven Development//), qui consiste à écrire en premier les tests et les utiliser comme point de départ pour écrire votre code. 
 + 
 +Il existe plusieurs types de tests, en fonction de ce que vous voulez tester et comment vous tester. 
 + 
 +  * les tests automatiques sont réalisés par des programmes, qui suivent un ensemble de tâches prédéfinies (appeler une fonction en utilisant des valeurs spécifiques, lire un fichier, telecharger des données en ligne, etc) puis vérifient que le résultat obtenu est conforme à ce que vous avez prévu. 
 +  * les tests manuels sont réalisés par des personnes, qui suivent une liste de tâches prédéfinies et vérifie le résultat obtenu. 
 + 
 +Les tests automatiques sont tres rapides a executer, alors que les tests manuels sont limités par la rapidité de celui qui fait les tests. (Et plus une personne se précipite, plus il risque de faire des erreurs). Il est donc préférable de privilégier les tests automatiques en priorité, mais ce n'est pas toujours possible. (Par exemple, si vous écrivez un programme qui va rechercher une image de chaton sur internet, il faudra bien qu'un humain vérifie l'image pour etre sur que c'est bien un chaton). 
 + 
 +Pour simplifier, plus il sera facile d'écrire des tests automatiques, plus un code sera testable. 
 + 
 +==== Tests unitaires ==== 
 + 
 +Un autre critère important de la testabilité est la granularité du code (c'est-a-dire a quelle point vous pouvez tester des parties minimalistes de votre code). Prenez un exemple simple : imaginez que vous devez écrire un code qui réalise deux tâches successives et retourne un résultat. 
 + 
 +<code cpp> 
 +int do_something() { 
 +    int result { 0 }; 
 +     
 +    // code pour la tâche 1 
 +    ... 
 + 
 +    // code pour la tâche 2 
 +    ... 
 +     
 +    return result; 
 +
 +</code> 
 + 
 +Vous devez tester si vous code est valide ou non. 
 + 
 +Dans cet exemple simple, le résultat attendu sera toujours le même, donc il est possible d'écrire un test qui appelle cette fonction et compare le résultat retourné. Par exemple : 
 + 
 +<code cpp> 
 +int main() { 
 +    const auto result { do_something() };  
 +    assert(result == expected_result); 
 +
 +</code> 
 + 
 +Ce simple code est un test : si vous exécutez ce code, si l'assertion est fausse, c'est que la fonction ne retourne pas le résultat attendu, le test a échoué. (En pratique, un vrai test est un peu plus complexe, vous testerez plus de choses en même temps. Et comme vous ferez beaucoup de tests en même temps, un outil spécialisé se chargera de regrouper et présenter les résultats des tests, pour faciliter l'analyse des tests. Mais le principe de base est le meme). 
 + 
 +Si le test échoue, cela va nécessiter une correction du code de votre part. La question est laquelle des deux tâches est incorrecte ? La premiere ? La seconde ? Les deux ? 
 + 
 +En faitce test ne permet pas de répondre à ces questions, parce qu'il ne teste pas séparément les deux tâches. On dit que le test n'est pas "unitaire" (il ne teste pas une chose unique). Plus un test est unitaire, plus il sera facile d'identifier la source d'une erreur et corriger le code. 
 + 
 +Il est facile de corriger le code précédent, en respectant le principe de responsabilité unique (SRP), c'est a dire en faisant une fonction pour chaque tâche. 
 + 
 +<code cpp> 
 +void task_1(int& value) {    
 +    // code pour la tâche 1 
 +    ... 
 +
 + 
 +void task_2(int& value) {    
 +    // code pour la tâche 2 
 +    ... 
 +
 + 
 +int do_something() { 
 +    int result { 0 }; 
 +    task_1(result); 
 +    task_2(result); 
 +    return result; 
 +
 + 
 +int main() { 
 +    // test de la tache 1 
 +    int result_1 { 0 }; 
 +    assert(task_1(result_1) == expected_result_1); 
 +     
 +    // test de la tache 1 
 +    int result_2 { 0 }; 
 +    assert(task_2(result_2) == expected_result_2); 
 +     
 +    // test de la tache 1 
 +    const int result { do_something() }; 
 +    assert(result == expected_result); 
 +
 +</code> 
 + 
 +Vous voyez qu'avec ce code, selon quelle assertion échoue, vous pourrez retrouver facilement quelle tâche n'est pas correcte. 
 + 
 +C'est le second critère de la testabilité : plus il sera facile d'écrire des tests unitaires, plus le code sera testable. 
 + 
 +Un code peu testable sera un code qui demandera plus de travail pour vérifier qu'il est correct et le maintenir. Voire ca sera un code qui a plus de risque de contenir des erreurs. 
 + 
 +Pour aller plus loin : [[https://en.wikipedia.org/wiki/Software_testability|Software testability]]. 
 + 
 + 
 +===== Classes, modules et bibliothèques logicielles ===== 
 + 
 +Un dernier critère important est le découpage du code. Si vous écrivez un code d'une dizaine de lignes, vous (ou un autre développeur) n'aurez probablement pas de mal à relire et comprendre votre code. Si vous écrivez une centaine de lignes de code, cela sera plus difficile. Si vous écrivez plusieurs milliers de lignes de code, vous aurez du mal a comprendre votre code. 
 + 
 +Les fonctions permettent de découper un code en partie plus courtes et plus simples a lire. En particulier, en donnant des noms aux fonctions et aux paramètres. Mais si votre code est conséquent, vous pourrez obtenir plusieurs milliers de fonctions et votre code redeviendra peu compréhensible. 
 + 
 +De nos jours, les programmes sont de plus en plus important en taille, développés par des équipes nombreuses, maintenu sur des années. Le découpage en fonctions n'est alors plus suffisant, il faut des niveaux supérieurs d'organisation du code : 
 + 
 +  * une classe regroupe plusieurs fonctions et structures de données ; 
 +  * un module regroupe plusieurs classes ayant une thématique commune (gestion des fichiers, réseaux, interface graphique, etc) ; 
 +  * une bibliothèque logicielle regroupe un ou plusieurs modules en projets, qui peuvent être développés de façon indépendante ; 
 +  * un framework regroupe plusieurs bibliothèques logicielles ; 
 +  * un programme va utiliser plusieurs bibliothèques logicielles pour remplir un ou plusieurs tâches spécifiques ;
  
 +L'application finale que vous proposerez aux utilisateurs pourra donc être un ensemble complexe d'applications qui interagissent en eux, de bibliothèques logicielles, de ressources (images, fichiers de données), etc.
  
-===== libs et modules =====+Ce qui implique qu'il faudra également gérer la création de //distribuables// (c'est-à-dire de "quelque chose" qui permettra aux utilisateurs d'accéder à vos applications : installation, mise à jour, fournir les sources, etc).
  
-la conception d'application vue comme la conception de libs+Pour terminer, en complément de cette organisation logique de votre code en fonctions, classes, modules, etc. il faudra également gérer l'organisation physique de votre code dans des fichiers et dans des répertoires.
  
  
conception.1492472622.txt.gz · Dernière modification: 2017/04/18 01:43 par gbdivers