code-generate : un outil générique de génération de code open source

code-generate est un outil générique de génération de code qui s'appuie sur des métadonnées variées et des modèles définis pour produire le code nécessaire, réduisant ainsi le travail de codage répétitif et de bas niveau. Il prend actuellement en charge la génération d'objets métier, d'objets d'accès aux données, etc., à partir de métadonnées de base de données.

Dépôts du projet

Utilisation

  1. Configurer un dépôt de modèles : créer ou utiliser un dépôt existant dans le répertoire src/resources/templateRepository. Voir l'exemple entityDemo.
  2. Exécuter mvn -U clean install pour générer le fichier JAR code-generate.jar.
  3. Lancer le JAR avec Java 17+ ou via les tests unitaires TestCommandLineApplication ou CommandLineApplication.

Exemple avec lecteur JSON (à des fins de test principalement)

Définir le fichier JSON de définition (voir definition.json), puis lancer le JAR (sous Windows, utiliser Cmd, PowerShell peut poser problème) :

java -jar code-generate.jar -Dtemplate.repository=entityDemo -Dgenerate.output=D:\source\code-generate\src\test\resources\generateResult -Dgenerate.basePackage=cn.cli -Dgenerate.author=liwei -Dgenerate.tags=mysql,jdbc -Dentity.reader=jsonFile -Dentity.reader.json.file=D:\source\code-generate\target\test-classes\entityGenerate\userDefinition.json

Utiliser un fichier de configuration :

java -jar code-generate.jar "-c=D:\source\code-generate\target\test-classes\config\json.properties"

Exemple avec lecteur JDBC

Créer une base de données et une table, puis exécuter :

java -jar code-generate.jar -Dtemplate.repository=entityDemo -Dgenerate.output=D:\source\code-generate\src\test\resources\generateResult -Dgenerate.basePackage=cn.cli.jdbc -Dentity.reader=jdbc -Dentity.names=user -Dentity.reader.jdbc.url=jdbc:mysql://localhost:3306/code_generate -Dentity.reader.jdbc.user=root -Dentity.reader.jdbc.password=123456

Avantages et inconvénients de la génération de code

Le principal avantage est la réduction du travail de codage répétitif et de bas niveau. Cependant, plusieurs inconvénients existent :

  • La génération initiale est simple, mais une fois le code modifié, la régénération devient complexe. Si le code généré n'est pas altéré, l'impact est limité.
  • Les générateurs « lourds » incitent souvent les développeurs à utiliser le code tel quel, sans l'adapter aux besoins réels.
  • Un inconvénient majeur est qu'ils peuvent masquer une architecture de mauvaise qualité, en apportant des rustines à une conception défaillante.
    • Par exemple, si une entité génère des dizaines de classes associées, un développeur refuserait de les écrire manuellement. Avec un générateur, on peut l'accepter, mais l'architecture devient difficile à modifier. L'outil agit alors comme un « réchauffeur de grenouille ».
    • De nombreuses technologies apparemment utiles souffrent du même problème, comme les conteneurs d'injection de dépendances qui peuvent produire des classes aux dépendances complexes sans que l'on s'en rende compte. Les outils techniques ne sauvent pas une mauvaise conception, ils ne font que la cacher plus profondément.

La génération de code doit être utilisée avec parcimonie : optimisez l'architecture et la conception autant que possible, et ne l'utilisez que pour les tâches répétitives vraiment inévitables.

Développement piloté par les tests (TDD)

Ce projet suit la méthode TDD.

Ressenti et effets

Une technique fluide pour organiser toutes les activités de développement

Le TDD (écrire les tests avant le code) est fluide :

  • Évite de se perdre dans des réflexions, des diagrammes ou du texte pour concevoir au préalable — cette approche est inefficace et limite l'imagination.
  • Évite de coder sans structure en commençant directement par l'implémentation.
  • Les tests unitaires permettent un refactoring continu, en maintenant une conception et un code de meilleure qualité.

Bottom-up

Le TDD est une approche de conception et de développement bottom-up : on commence par des aspects précis et simples, jusqu'aux problèmes les plus complexes, pour atteindre l'objectif final. Face à des sujets nouveaux et complexes, il est difficile de donner une conception complète top-down. Le bottom-up offre une manière progressive de résoudre des sous-problèmes et de les intégrer jusqu'à la solution finale.

Codage, conception et tests à chaque niveau

Avec le TDD, on peut tester, coder et concevoir un concept à tous les niveaux, en s'assurant que chaque couche est adaptée. Par exemple, dans targetLanguage, on passe de EntityType à Property puis à Entity et enfin à CodeGenerate.

On teste du niveau le plus élevé au plus bas. Si un problème survient, on examine quels tests échouent pour identifier la source. Si les tests de haut niveau passent, le problème vient probablement d'une valeur mal transmise ou d'une modification dans une couche supérieure. Cette conception de targetLanguage permet de réduire considérablement le temps de débogage.

Volume de code des tests unitaires

Le volume de code des tests est comparable à celui de l'implémentation : une fois, sur 1 714 lignes d'implémentation, 1 400 lignes de tests (plugin Statistic d'IntelliJ). Cela peut sembler augmenter le travail, mais en réalité cela réduit le temps de débogage, rend le code plus propre, facilite le refactoring, diminue le nombre de bogues et réduit les coûts de développement et de maintainance.

Essence du TDD

Voici quelques citations classiques qui illustrent l'essence du TDD :

  • Extrait de Test-Driven Development (Kent Beck) : « On m'a appris ‹ Code pour aujourd'hui, conçois pour demain ›. Le TDD renverse cela : code pour demain, conçois pour aujourd'hui. »
  • « Le test (Test) — automatique, concret, tangible. Une simple pression sur une touche l'exécute. Ironiquement, le TDD n'est pas une technique de test (Cunningham Koan). C'est une technique d'analyse, de conception, et surtout une technique pour organiser toutes les activités de développement. »

Extrait de Clean Agile: Back to Basics sur la comptabilité en partie double :

  • « Les comptables ont inventé une règle il y a 1 000 ans : la comptabilité en partie double. Chaque transaction est enregistrée deux fois : un crédit dans un compte, un débit correspondant dans un autre. Les soldes sont réconciliés dans un bilan : actif total moins passif total et capitaux propres. Le solde doit être nul. S'il ne l'est pas, il y a une erreur. »
  • « La comptabilité en partie double et le TDD sont des disciplines équivalentes. Elles ont la même fonction : éviter les erreurs dans des documents d'importance cruciale, s'assurer que chaque symbole est correct. Bien que la programmation soit devenue indispensable pour la société, nous n'avons pas encore imposé le TDD par la loi. Mais alors que des logiciels mal écrits causent déjà des pertes de vies et de biens, la législation serait-elle loin ? »

Étapes

On commence par une liste de tâches (idées de conception, cas d'utilisation à implémenter, refactoring, etc.). Chaque idée est notée simplement, on raye les tâches accomplies.

Ensuite, le cycle :

  1. Choisir une tâche, écrire un test unitaire qui échoue (y compris les erreurs de compilation ou d'exécution).
  2. Faire passer le test.
  3. Refactoriser le code pour améliorer la conception.

Répéter jusqu'à épuisement des tâches.

Structure des packages et classes principales

Package entity

Contient les classes liées à la définition des entités : Entity, Property, EntityType.

Sous-package entity.reader

Lecture de la configuration des entités pour la génération de code. Types supportés :

  • jsonFile : lit les métadonnées depuis un fichier JSON.
  • jdbc : lit les métadonnées depuis une base de données (MySQL, Oracle, etc.) via JDBC.

Package template

Moteur de templates et instances de templates. Moteur actuel :

Package param

Gestion des paramètres depuis les fichiers de configuration.

  • Support de @file dans un fichier pour spécifier le nom du fichier généré.
  • Support de @folder pour spécifier le répertoire de sortie (souvent combiné avec le paramètre output).

Package generate

Classes centrales de la génération de code.

  • CodeGenerate : point d'entrée, lit tous les paramètres.
  • CodeGenerateWork : classe de travail qui exécute la génération.
  • Implémentations concrètes :
    • EntityCodeGenerateWork : génère des fichiers à partir des configurations d'entité et des modèles.

Étiquettes: code-generate génération de code métadonnées JDBC JSON

Publié le 10 juin à 04h28