Hoja de repaso: Optimiser la Qualité du Code

📋 Plan du Cours

  1. Suite de Fibonacci et nombre d’or
  2. Lisibilité du code et intention
  3. Nommage explicite des variables et méthodes
  4. Recommandations pour un code expressif
  5. Standards de codage et formatage
  6. Commentaires pertinents et documentation par tests
  7. Code smells et règle des boy-scouts
  8. Refactoring avec tests et catalogue de refactorings
  9. Règles de simplicité et réduction de duplication
  10. Dette technique et complexité cyclomatique
  11. Inspection continue avec SonarQube
  12. DRY, GRASP et loi de Demeter

📖 1. Suite de Fibonacci et nombre d’or

🔑 Notions clés & Définitions

  • Suite de Fibonacci : Suite d’entiers où chaque terme est la somme des deux précédents, utilisée pour relier croissance et proportion.
  • Nombre d’or : Constante liée aux proportions, souvent présentée comme la limite des rapports successifs dans la suite de Fibonacci.
  • Correspondance Fibonacci et nombre d’or : Lien entre la suite de Fibonacci et le nombre d’or, utilisé pour expliquer pourquoi les rapports de termes se stabilisent.
  • Croissance par couples de Fibonacci : Modèle de reproduction où chaque couple engendre un nouveau couple à partir d’un âge donné, produisant une suite de type Fibonacci.

📝 Points essentiels

  • Dans un modèle de reproduction, si chaque couple crée un nouveau couple à compter du troisième mois de son existence, le nombre de couples suit une logique de Fibonacci.
  • La suite de Fibonacci est associée au nombre d’or via l’idée que les rapports de termes successifs convergent vers cette constante.
  • Le contenu source renvoie à une ressource de référence sur la suite de Fibonacci pour approfondir la définition et les propriétés.
  • Le contenu source renvoie à une ressource sur la formule liée à l’harmonie et au nombre d’or pour relier proportion et Fibonacci.
  • Le lien « Fibonacci et nombre d’or » est explicitement présenté comme une correspondance à connaître pour comprendre la stabilisation des proportions.

💡 Astuce mémo

Fibonacci → rapports qui se stabilisent : Fibonacci donne le nombre d’or.

📖 2. Lisibilité du code et intention

🔑 Notions clés & Définitions

  • Lisibilité du code : La lisibilité du code mesure à quel point le programme est compréhensible rapidement grâce à une structure claire et des noms explicites.
  • Intention du code : L’intention du code est le sens de ce que fait chaque variable et chaque bloc, rendu visible par des noms adaptés plutôt que par des devinettes.
  • Code smells : Les code smells désignent des indices de mauvaise qualité qui rendent le code plus difficile à comprendre, étendre ou modifier.
  • Refactoring : Le refactoring est l’ensemble des transformations qui améliorent la structure du code sans changer son comportement.
  • Renommage de variables : Le renommage de variables consiste à remplacer des noms vagues par des noms qui reflètent le rôle réel des données et des calculs.

📝 Points essentiels

  • Renommer les variables sert à rendre l’intention explicite et à améliorer la compréhension du code.
  • Un code plus lisible est plus facile à étendre et à modifier, car le rôle des éléments est devinable sans effort excessif.
  • Le refactoring vise à limiter les code smells, ce qui aide à faire émerger une conception plus simple.
  • Le code initial avec des noms comme a1, a2, a3, a5, a6 masque le sens des paramètres, de l’itération et du résultat.
  • Le renommage relie directement les variables à leur rôle (ex. numResult, collection, count, result, num) pour expliquer le comportement du programme.
  • Le renommage peut aussi clarifier la logique de comptage (ex. skipped) en séparant ce qui est imprimé de ce qui est ignoré.

💡 Astuce mémo

Noms = sens : si tu peux expliquer “ce que fait le code” uniquement avec les noms, la lisibilité est bonne.

📖 3. Nommage explicite des variables et méthodes

🔑 Notions clés & Définitions

  • Nommage explicite : Le nommage explicite consiste à choisir des identifiants qui décrivent clairement le rôle et l’intention d’une variable ou d’une méthode.
  • Responsabilités partagées : Les responsabilités partagées consistent à répartir le travail entre des éléments du code pour que chaque partie ait un rôle compréhensible.
  • Méthode isFlagged : isFlagged est une méthode qui encapsule la logique permettant de savoir si une cellule est marquée (flagguée) ou non.
  • getFlaggedCell : getFlaggedCell est une méthode qui renvoie la liste des cellules dont l’état correspond à la condition de “flag”.
  • getFlg : getFlg est une méthode dont le nom peu explicite masque l’intention réelle de filtrage et de retour d’un sous-ensemble.

📝 Points essentiels

  • Renommer f1 en printFirstNPositive rend l’intention “afficher les N premières valeurs positives” plus visible que le nom générique f1.
  • Renommer a1 en n, a2 en c, a3 en i, a5 en result, et a6 en num clarifie le rôle de chaque variable dans la boucle et le comptage.
  • Dans l’exemple avec int[] et index 0, la condition x[0] == 4 dépend d’une convention implicite sur ce que représente l’élément d’index 0.
  • Un nommage explicite aide à contextualiser le code et à révéler clairement ce que fait chaque bloc (filtrage, comptage, affichage).
  • Le passage de getFlg à getFlaggedCell illustre un nom de méthode qui décrit le résultat renvoyé (cellules “flagguées”).
  • Le renommage x → cell et list1 → flaggedCells rend la boucle de filtrage plus compréhensible en liant variable et signification métier.

💡 Astuce mémo

Si le nom dit l’intention (printFirstNPositive, getFlaggedCell), le code “se lit” sans deviner les rôles (n, i, num, flaggedCells).

📖 4. Recommandations pour un code expressif

🔑 Notions clés & Définitions

  • Nommage hongrois : Technique de nommage où le nom encode le type ou la nature de la variable, au lieu de refléter son rôle métier.
  • Ubiquitous language : Langage partagé entre métier et équipe, qui stabilise le vocabulaire pour que les noms de code correspondent aux concepts du domaine.
  • Word cloud : Visualisation qui regroupe les mots d’un corpus, utile pour repérer rapidement les noms dominants dans un code source.
  • Moteur de synonymes : Outil qui propose des termes proches pour aider à choisir un nom métier plus précis quand l’inspiration manque.
  • Standards de codage : Ensemble de règles de style qui impose une présentation et une structure cohérentes du code (ex. conventions de nommage et de formatage).

📝 Points essentiels

  • Éviter la notation hongroise car elle masque l’intention métier et pousse à nommer par le type plutôt que par le rôle.
  • Ne pas tomber dans l’excès inverse : un nom trop court ou trop long nuit à la lisibilité, donc viser un équilibre entre précision et concision.
  • Renommer les variables et extraire une nouvelle classe responsable de la cellule (ex. classe Cell avec une méthode isFlagged) pour rendre le code plus compréhensible.
  • Vérifier la pertinence du nommage dans les classes métiers via un word cloud pour repérer les termes réellement mis en avant.
  • Utiliser un moteur de synonymes (ex. synonymy.com ou thesaurus.com) pour trouver le bon terme métier quand le vocabulaire n’est pas clair.
  • S’appuyer sur des standards de codage (ex. Java Google Style) pour montrer l’intention et formater correctement le code.

💡 Astuce mémo

Hongrois = type, Expressif = rôle métier ; Word cloud = mots dominants ; Standards = intention + format.

📖 5. Standards de codage et formatage

🔑 Notions clés & Définitions

  • Google Java Style : Le standard de codage Java de Google qui décrit des règles de style et de formatage pour rendre le code cohérent et lisible.
  • Code Conventions Java Oracle : L’ensemble de conventions de codage Java publié par Oracle qui fixe des recommandations de structure, de nommage et de style.
  • Style guides Google : La collection de guides de style Google pour plusieurs langages, chacun précisant des conventions adaptées au langage concerné.
  • Limitation des commentaires : La pratique consistant à éviter les commentaires inutiles et à ne garder que ceux qui apportent une information introuvable dans le code.
  • Commentaires WHY et HOW : Une approche de commentaire qui distingue l’intention (WHY) de la mécanique (HOW) pour améliorer la compréhension.

📝 Points essentiels

  • Un retour sur l’architecture et la méthodologie mentionne l’idée de montrer l’intention et de formater correctement le code.
  • Les standards peuvent être fournis par l’équipe ou être inspirés de standards existants comme Google Style ou les conventions Oracle pour Java.
  • Google Java Style est accessible via la page officielle du guide Java de Google.
  • Les Google Style Guides sont disponibles sur GitHub et couvrent plusieurs langages (ex. C++, Objective-C, Python, Shell, HTML/CSS, JavaScript, etc.).
  • Pour les commentaires, la règle implicite est de ne pas surcharger : un commentaire doit être pertinent et apporter une valeur réelle.
  • Les commentaires sont discutés via des exemples de “code commenté” vs “code trop commenté”, avec l’idée que le code doit rester compréhensible sans bavardage inutile.

💡 Astuce mémo

Standards = cohérence (Google/Oracle) ; Commentaires = valeur (WHY/HOW), pas du bruit.

📖 6. Commentaires pertinents et documentation par tests

🔑 Notions clés & Définitions

  • Commentaires pertinents : Notion de rédaction où les commentaires servent à clarifier l’intention ou le pourquoi, sans répéter le code.
  • Documentation par tests : Approche où les tests décrivent le comportement attendu du code et deviennent une forme de documentation exécutable.
  • Code smells : Notion de défauts de code qui signalent un problème de conception, de lisibilité ou de maintenabilité.
  • Refactoring : Notion de transformation du code qui améliore sa structure pour limiter les code smells sans changer le comportement.
  • Lisibilité du code : Caractéristique d’un code compréhensible rapidement, permettant lecture et évolution par d’autres développeurs.

📝 Points essentiels

  • Les commentaires doivent compléter le code en expliquant l’intention ou le contexte, pas en reformulant ce que le code dit déjà.
  • Les tests peuvent servir de documentation en montrant comment le système doit se comporter dans des cas concrets.
  • Un code bien nommé rend souvent les commentaires moins nécessaires car l’intention devient plus explicite.
  • Le refactoring aide à limiter les code smells en faisant émerger une conception plus simple.
  • Les code smells sont des signaux fréquents quand le code n’est pas clean, et ils peuvent guider les améliorations.
  • La lisibilité est un critère central du code clean, car elle facilite la lecture et l’amélioration par d’autres développeurs.

💡 Astuce mémo

Tests = “documentation qui s’exécute” : si ça passe, le comportement attendu est prouvé.

📖 7. Code smells et règle des boy-scouts

🔑 Notions clés & Définitions

  • Code smell : Un code smell est un signe de mauvaise conception ou de code fragile qui augmente le risque de bugs et complique la maintenance.
  • Magic number : Un magic number est une valeur littérale numérique (ou constante) écrite directement dans le code sans nom explicite, ce qui rend l’intention difficile à lire.
  • Duplication : La duplication est la répétition de code ou de logique à plusieurs endroits, ce qui rend les corrections coûteuses et incohérentes.
  • Primitive obsession : La primitive obsession est l’abus de types primitifs (ex. nombres, chaînes) au lieu de types plus expressifs, ce qui affaiblit la modélisation.
  • Mixed concerns : Mixed concerns désigne le mélange de responsabilités différentes (par exemple technique et métier) dans le même code, rendant l’évolution plus difficile.

📝 Points essentiels

  • Le code présenté contient des indices de code smells comme des valeurs numériques en dur, des répétitions de calculs et des conditions imbriquées.
  • La règle des boy-scouts impose de laisser le code dans un état meilleur que celui trouvé, ce qui pousse à améliorer en continu lors des modifications.
  • La démarche recommandée pour traiter un code smell est le refactoring, c’est-à-dire un remaniement interne sans changer le comportement attendu.
  • Le refactoring doit s’appuyer sur des tests : sans tests, on ne peut pas vérifier que le comportement reste identique après modification.
  • Le refactoring vise à réduire la complexité et à clarifier la structure interne, plutôt qu’à ajouter de nouvelles fonctionnalités.

💡 Astuce mémo

Boy-scouts = « on améliore en partant » ; Refactoring = « sans tests, pas de départ ».

📖 8. Refactoring avec tests et catalogue de refactorings

🔑 Notions clés & Définitions

  • Refactoring : Le refactoring est une modification de la structure interne d’un logiciel sans changer son comportement observable.
  • Code smell : Un code smell désigne un signe de code qui indique un problème de conception ou de maintenabilité, souvent corrigé par refactoring.
  • Catalogue de refactorings : Un catalogue de refactorings regroupe des techniques de remaniement du code, chacune associée à un objectif et à un type de problème.
  • Tests automatisés : Les tests automatisés vérifient automatiquement le comportement du logiciel afin de sécuriser les changements lors d’un refactoring.
  • Règles de simplicité : Les règles de simplicité décrivent des critères concrets pour obtenir un code lisible, peu redondant et peu complexe.

📝 Points essentiels

  • Pas de refactoring sans tests : les tests servent de filet de sécurité pour vérifier que le comportement observable ne change pas.
  • Un refactoring vise la structure interne, pas la modification des résultats attendus par l’utilisateur ou par les tests.
  • Le catalogue de refactorings sert de boîte à outils : il propose des techniques à appliquer quand un code smell apparaît.
  • Les techniques de refactoring sont présentées comme un ensemble de remaniements, à choisir selon le problème rencontré.
  • Les règles de simplicité (XP) incluent : tous les tests passent, ce qui garantit la stabilité pendant l’évolution.
  • Les règles de simplicité (XP) incluent : révéler l’intention du code pour améliorer la lisibilité et la compréhension rapide du lecteur.

💡 Astuce mémo

Tests d’abord → refactor ensuite : si tous les tests passent, le comportement observable est préservé.

📖 9. Règles de simplicité et réduction de duplication

🔑 Notions clés & Définitions

  • Conception simple : Approche visant à obtenir un système compréhensible et maintenable, en privilégiant la clarté et en évitant les mécanismes inutiles.
  • Simplicité non triviale : Idée que la simplicité est difficile à produire en pratique, car elle demande des choix et des arbitrages continus, pas seulement une intention.
  • Réduction de duplication : Principe consistant à limiter la répétition de logique ou de structure pour diminuer les incohérences et faciliter l’évolution du code.
  • Dette technique : Métaphore du coût accumulé par des choix de conception ou de code qui rendent le futur plus difficile, plus risqué ou plus coûteux.
  • Refactoring : Enchaînement d’étapes qui améliore la structure du code sans changer son comportement, pour le rendre plus propre et réduire la dette technique.

📝 Points essentiels

  • La simplicité est présentée comme un objectif à construire, pas comme un résultat automatique, car sa mise en œuvre demande de la discipline et des compromis.
  • La réduction de duplication vise à diminuer les zones où des modifications doivent être répétées, ce qui réduit les risques d’écarts entre versions du même comportement.
  • La dette technique est décrite comme une métaphore inventée par Ward Cunningham pour parler des conséquences à long terme de décisions de développement imparfaites.
  • La dette technique rend l’expérience des développeurs plus pénible et le travail plus laborieux, avec un effet qui peut s’installer progressivement.
  • Les étapes de refactoring permettent de travailler sur un code plus propre (clean code) et de minimiser la dette technique.
  • La métaphore des grenouilles illustre un effet de seuil : une augmentation progressive de la “température” laisse le problème s’installer jusqu’à un point où l’extraction devient difficile, alors qu’une entrée brutale en

💡 Astuce mémo

Simplicité = effort continu ; Duplication = dette cachée ; Grenouilles = montée progressive = cuisson lente, refactoring = sortie avant l’eau bouillante.

📖 10. Dette technique et complexité cyclomatique

🔑 Notions clés & Définitions

  • Dette technique : Dette technique : ensemble des compromis de qualité qui rendent le code plus difficile à maintenir et à faire évoluer.
  • Refactoring : Refactoring : ensemble d’étapes qui améliorent la structure du code sans changer son comportement, pour le rendre plus propre.
  • Complexité cyclomatique : Complexité cyclomatique : mesure du nombre d’embranchements logiques dans le code, utilisée pour repérer les zones difficiles à maintenir.
  • Décompte des embranchements : Décompte des embranchements : comptage des points de décision du code (conditions et alternatives) pour estimer la complexité cyclomatique.

📝 Points essentiels

  • Le refactoring permet au développeur de travailler sur un code plus propre (clean code) et de réduire la dette technique.
  • Sans refactoring, la dette technique s’accumule et finit par se manifester dans la maintenance du code.
  • La complexité cyclomatique correspond au décompte des embranchements dans le code (if, for, while, case, catch, throw, return, opérateur ||).
  • Une complexité cyclomatique élevée peut signaler une classe complexe à maintenir.
  • Exemple if/else : pour 7 jours, la complexité calculée passe de 7 (cas Lundi) à 13 (cas Dimanche) selon le nombre de tests réellement traversés.

💡 Astuce mémo

Refactoring = nettoyage qui freine la dette ; Complexité cyclomatique = compteur d’embranchements (if/for/while/case/||) qui révèle les classes difficiles.

📖 11. Inspection continue avec SonarQube

🔑 Notions clés & Définitions

  • Inspection continue : Approche de contrôle qualité qui analyse le code en continu pour détecter rapidement les problèmes et suivre leur évolution.
  • SonarQube : Plateforme d’analyse de code qui produit un tableau de bord et des issues pour évaluer la qualité logicielle.
  • Dashboard SonarQube : Vue synthétique qui regroupe les indicateurs qualité et les issues afin de piloter le traitement des problèmes.
  • Issues SonarQube : Enregistrements des problèmes détectés par l’analyse, utilisés pour prioriser et corriger le code.
  • Clean Code : Ensemble de bonnes pratiques visant à écrire un code maintenable et compréhensible, souvent évalué par des règles d’analyse.

📝 Points essentiels

  • SonarQube s’appuie sur une inspection continue pour tester la qualité du code et remonter des problèmes sous forme d’issues.
  • Le tableau de bord (dashboard) sert de point d’entrée pour visualiser les indicateurs et les issues à traiter.
  • Une “zoom” sur les éléments du dashboard permet d’examiner plus finement les issues liées au code analysé.
  • Les bonnes pratiques de Clean Code citées incluent DRY, GRASP et la loi de Demeter, utilisées comme repères de qualité.
  • La section mentionne des ressources SonarSource et SonarQube (white paper, media kit, site officiel) pour approfondir l’inspection continue.

💡 Astuce mémo

Dashboard = “table de bord” : indicateurs + issues, donc tu pilotes la correction en continu.

📖 12. DRY, GRASP et loi de Demeter

🔑 Notions clés & Définitions

  • DRY : Principe de conception qui vise à éviter la duplication de logique en factorisant le code pour réduire les incohérences et les coûts de maintenance.
  • GRASP : Ensemble de principes de responsabilité logicielle qui aide à répartir les responsabilités entre objets pour rendre le design plus cohérent.
  • Loi de Demeter : Principe de connaissance minimale qui limite les interactions d’un objet à ses « amis » directs pour éviter les chaînes d’appels trop dépendantes.
  • Principe de connaissance minimale : Variante formulée de la loi de Demeter qui impose de ne demander que ce que l’on peut obtenir via des relations immédiates, sans inspecter des objets internes.

📝 Points essentiels

  • La loi de Demeter recommande de ne parler qu’aux amis immédiats d’un objet, plutôt que d’aller chercher des informations au travers d’une longue chaîne d’objets.
  • Le scénario du pantalon illustre l’idée de fournir directement ce qui est demandé, sans obliger l’autre à fouiller dans vos poches pour trouver ce dont il a besoin.
  • Exemple non conforme : auto.getMotor().getState().isRunning() peut échouer si l’état « Running » n’est plus exposé au niveau du moteur mais au niveau des roues.
  • Exemple conforme : auto.isRunning() respecte la loi en déléguant la connaissance interne à l’objet Auto, au lieu de traverser ses composants.
  • Règle de style associée à l’exemple : un code conforme limite les enchaînements et évite de multiplier les points d’accès par ligne.
  • Comparaison : la loi de Demeter réduit la dépendance aux détails internes (moteur/roues) en exposant une interface simple, tandis que l’accès direct aux sous-objets rend le code fragile aux changements d’implémentation.

💡 Astuce mémo

Demeter = « amis immédiats » : si tu dois traverser plusieurs objets (moteur → état → isRunning), tu t’éloignes de la loi.

📊 Tableaux de synthèse

Correspondances de qualité : lisibilité vs duplication vs complexité

AspectObjectifIndicateur/repère
Lisibilité du codeRendre l’intention visible et compréhensible rapidementNommage explicite (intention) et code clean
DuplicationRéduire les incohérences et les coûts de maintenancePrincipe DRY (pas de duplication)
ComplexitéRepérer les zones difficiles à maintenirComplexité cyclomatique = décompte des embranchements (if/for/while/case/catch/throw/return/||)

⚠️ Pièges & confusions fréquents

  1. Confondre refactoring et ajout de fonctionnalités : le refactoring change la structure interne sans modifier le comportement observable.
  2. Croire que les commentaires doivent répéter le code : ici, ils doivent apporter une info introuvable dans le code (WHY/HOW), sinon ils deviennent du bruit.
  3. Prendre la notation hongroise comme une bonne pratique : le cours dit d’éviter car elle masque l’intention métier et pousse à nommer par le type.
  4. Interpréter la loi de Demeter comme “moins de méthodes” : c’est surtout limiter les chaînes d’appels et parler uniquement aux amis immédiats (ex. auto.isRunning()).
  5. Confondre code smells et bugs : un code smell est un signe de mauvaise conception/fragilité qui augmente le risque de bugs et complique la maintenance.
  6. Mal calculer la complexité cyclomatique : elle correspond au décompte des embranchements, pas au nombre de lignes, et inclut notamment ||.
  7. Penser que “sans tests” on peut refactorer : le cours insiste sur “pas de refactoring sans test” pour vérifier que le comportement reste identique.

✅ Checklist Examen

  1. Expliquer ce qu’est la suite de Fibonacci et le lien “Fibonacci et nombre d’or” (stabilisation des rapports).
  2. Décrire le modèle de reproduction : chaque couple engendre un nouveau couple à compter du troisième mois, et relier cela à une logique de type Fibonacci.
  3. Définir lisibilité du code et intention du code, et relier la lisibilité à la facilité d’extension/modification.
  4. À partir de l’exemple f1, justifier pourquoi un renommage (ex. f1 → processElements, a1 → numResult, a2 → collection) rend l’intention explicite.
  5. À partir de l’exemple printFirstNPositive, expliquer le rôle de skipped et pourquoi le renommage clarifie le comptage (ce qui est imprimé vs ignoré).
  6. Expliquer la différence entre getFlg et getFlaggedCell : le nom doit décrire le résultat renvoyé (cellules “flagguées”).
  7. Expliquer pourquoi l’accès à x[0] == 4 dans un tableau d’int[] masque une convention implicite, et comment une classe Cell avec isFlagged rend le code plus contextualisé.
  8. Citer les recommandations pour un code expressif : éviter la notation hongroise, viser un équilibre entre noms trop courts et trop longs, et utiliser word cloud + moteur de synonymes + ubiquitous language.
  9. Donner les repères de standards de codage cités (Google Java Style, Code Conventions Oracle) et expliquer l’idée “montrer l’intention & formater correctement”.
  10. Expliquer quand et pourquoi écrire des commentaires : limiter les commentaires, distinguer WHY (pourquoi) et HOW (comment), et éviter le “code trop commenté”.
  11. Définir code smell, magic number, duplication, primitive obsession, mixed concerns, et relier leur traitement au refactoring (avec tests).
  12. Expliquer la règle des boy-scouts et les règles de simplicité (XP) : tous les tests passent, révéler l’intention, pas de duplication (DRY), et réduire la complexité.
  13. Définir dette technique (métaphore de Ward Cunningham) et expliquer la métaphore des grenouilles pour illustrer l’accumulation progressive.
  14. Définir complexité cyclomatique et savoir quels embranchements comptent (if/for/while/case/catch/throw/return/||), puis interpréter pourquoi une complexité élevée signale une classe difficile à maintenir.

Pon a prueba tus conocimientos

Pon a prueba tus conocimientos sobre Optimiser la Qualité du Code con 24 preguntas de opción múltiple con correcciones detalladas.

1. Dans une suite de Fibonacci, comment se construit chaque terme suivant ?

2. Que représente le lien entre la suite de Fibonacci et le nombre d’or ?

Realiza el cuestionario →

Repasa con tarjetas de memoria

Memoriza los conceptos clave de Optimiser la Qualité du Code con 24 tarjetas de memoria interactivas.

Suite de Fibonacci — définition ?

Séquence d’entiers où chaque terme est la somme des deux précédents.

Nombre d’or — lien avec Fibonacci ?

Limite des rapports successifs de Fibonacci.

Lisibilité du code — rôle ?

Faciliter la compréhension rapide par une structure claire.

Ver tarjetas de memoria →

Similar courses

Crea tus propias hojas de repaso

Importa tu curso y la IA genera hojas, cuestionarios y tarjetas de memoria en 30 segundos.

Generador de hojas