L'Antre d'une Geekette

Cours de programmation, code créatif,
intelligence artificielle et trucs rétro
avec des peluches !

Cours de programmation, code créatif,
intelligence artificielle et trucs rétro
avec des peluches !

samedi 5 avril 2025

Code plus vite que l'IA, sans IA !

Cours destinés aux développeurs de niveau intermédiaire.
Nécessite des bases en Javascript, HTML et CSS ainsi qu'en programmation orientée objet et SQL.

Bonjour tout le monde !

À l'heure où les média vantent la puissance de l'IA, certains développeurs ne jurent plus que par elle pour accélérer leur productivité et promettent un avenir sombre à ceux qui ne s'y mettent pas.

Et si je vous disais qu'il est possible de coder plus vite et mieux qu'une IA...

Sans IA !

Vous y croyez, vous ?

Vous allez voir, c'est fabuleusement simple !

On va pas se mentir, de nos jours, le mot "IA" est devenu un mot magique sur le plan marketing.
Et quand un produit fait l'objet d'un effet "tout nouveau tout beau" comme ça, on a tendance à l'utiliser partout, pour tout et n'importe quoi et surtout, là où ce n'est pas pertinent.

Sauf qu'à un moment, le boulot d'un ingénieur, ce n'est pas de suivre une mode aveuglément, mais de réfléchir à la solution la plus adaptée à un besoin.

J'ai une tâche à automatiser. Est-ce que je n'ai pas un outil plus simple, plus rapide, plus fiable et moins coûteux pour la réaliser ?

En ce qui concerne l'écriture du code, la réponse est oui, et on va même le fabriquer ensemble cet outil. 😁

Je vous propose de développer un ensemble de petit outils pour générer du code en Java, ainsi que du code SQL.

Pourquoi c'est 2 langages en particulier ?

La première raison, c'est qu'ils sont très courant sur le marcher du travail. Vous aurez du mal à les esquiver si vous vous engagez dans une carrière de développeur.

Ensuite, concernant Java, c'est un langage de programmation orienté objet, ce qui est le paradigme de programmation majoritaire de nos jours, et il est fortement typé.
La première caractéristique va vous permettre d'adapter votre outil à un très grand nombre de langages.
La seconde va vous pousser à développer un outil complet. Il sera donc plus facile à adapter pour des langages plus souples parce que vous aurez juste à enlever des choses au lieu d'en ajouter.
Notez que je n'irai pas jusqu'à implémenter les concepts avancés de classe abstraite et d'interface, mais vous pouvez tout à fait enrichir le code que je vais vous proposer pour les inclure.

Concernant SQL, il s'agit du principal langage de gestion de base de données et de nos jours, la donnée constitue le nerf de la guerre.
80% des offres d'emplois en développement relèvent de l'informatique de gestion et donc implique des interactions avec des bases de données.
En fait, dès que vous devez créer un compte quelque part ou que vous pouvez poster un commentaire, il y a nécessairement une base de données, donc elles sont vraiment partout ! 🤪

Marine Le Pen : "Ils sont partout ! "

Générer le code d'une classe

On va commencer simple avec un outil qui génère une classe Java à partir d'un JSON d'entrée. On va le coder en Javascript dans un fichier HTML. L'intérêt de faire ça en Javascript c'est que c'est rapide à coder, facile à distribuer, facile à lancer (juste ouvrir un fichier HTML dans un navigateur). Vous pouvez embarquer votre outil sur une clé USB et le faire tourner sur n'importe quelle machine du moment qu'elle dispose d'un navigateur web

Pour commencer, créez un fichier HTML que vous pouvez nommer JavaClass.html. Ensuite, copiez-y le code HTML ci-dessous.
Il s'agit du code de l'interface graphique. Elle est très simple. Elle comprend un champ texte dans lequel vous saisirez le JSON d'entrée, un champ d'affichage du code généré, un bouton pour générer le code et enfin un bouton pour copier le code généré.

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8" />
		<title>Java class generator</title>

		<!-- Le CSS qui structure l'interface -->
		<style>

			body
			{
				position: absolute;
				left: 0px;
				right: 0px;
				top: 0px;
				bottom: 0px;
			}

			#input, #code-block
			{
				position: absolute;
				top: 10px;
				bottom: 40px;
			}

			#input
			{
				left: 10px;
				right: calc(50% + 7px);
				resize: none;
			}

			#code-block
			{
				left: calc(50% + 7px);
				right: 10px;
				margin: 0px;
				padding: 10px;
				border: 1px solid black;
				overflow: auto;
			}

			#create-code-block, #copy-code-block
			{
				position: absolute;
				bottom: 0px;
				height: 30px;
				margin: 0px;
				text-align: center;
			}

			#create-code-block
			{
				left: 10px;
				right: calc(50% + 7px);
			}

			#copy-code-block
			{
				left: calc(50% + 7px);
				right: 10px;
			}

		</style>
	</head>

	<body>
		<!-- Champ de saisie du JSON d'entrée -->
		<textarea id="input" >

			<!-- On va écrire ici un exemple de JSON attendu -->

		</textarea>
		
		<!-- Bloc qui affiche le code généré -->
		<pre id="code-block" ></pre>
		
		<!-- Bouton qui déclenche la génération du code -->
		<p id="create-code-block" >
			<input id="create-code" type="button" value="Create code" />
		</p>

		<!-- Bouton qui déclenche la copy du code généré -->
		<p id="copy-code-block" >
			<input id="copy-code" type="button" value="Copy code" />
		</p>

		<script type="text/javascript" >

			// C'est ici que l'on va écrire le Javascript qui génère le code.

		</script>
	</body>
</html>

Ensuite, on va élaborer le schéma du JSON attendu en entrée.

Pour commencer, une classe a besoin d'un nom, donc on ajoute une premier paramètre qui renseigne cette information.

{
	"className": "Example"
}

Ensuite, on ajoute un paramètre pour indiquer si notre classe hérite d'une autre classe. Si ce n'est pas le cas, vous mettez la valeur null.

{
	"className": "Example",
	"extends": "MotherClass"
}

Ensuite, on ajoute le paramètre des attributs de la classe. Il s'agit d'un tableau qui contient autant d'éléments que d'attributs.
Chaque attribut comporte des paramètres renseignant son nom, son type, sa valeur par défaut, son accessibilité (private, protected ou public), si l'attribut est un des paramètres du constructeur par défaut, si l'attribut est statique ou non et enfin si l'attribut est une constante ou non.

{
	"className": "Example",
	"extends": "MotherClass",
	"attributes":
	[
		{
			"name": "integerVariable",
			"type": "int",
			"default": 0,
			"accessibility": "private",
			"constructorParam": true,
			"isStatic": false,
			"isConst": false
		}
	]
}

Et enfin, on ajoute le paramètre des méthodes de la classe. Il s'agit d'un tableau qui contient autant d'éléments que de méthodes.
Chaque méthode comporte des paramètres renseignant son nom, le type qu'elle retourne, son accessibilité (private, protected ou public), si la méthode est statique ou non et enfin un tableau contenant la liste de ses paramètres d'entrée.

{
	"className": "Example",
	"extends": "MotherClass",
	"attributes":
	[
		{
			"name": "integerVariable",
			"type": "int",
			"default": 0,
			"accessibility": "private",
			"constructorParam": true,
			"isStatic": false,
			"isConst": false
		}
	],
	"methods":
	[
		{
			"name": "method1",
			"returnType": "int",
			"accessibility": "private",
			"isStatic": false,
			"params":
			[
				{ "name": "param1", "type": "int" },
				{ "name": "param2", "type": "float" },
				{ "name": "param3", "type": "double" },
				{ "name": "param4", "type": "String" },
				{ "name": "param5", "type": "boolean" }
			]
		}
	]
}

Voilà, nous disposons d'un exemple complet de JSON d'entrée.
Copiez-le dans la balise textarea du code HTML. Ainsi, le champ d'entrée sera toujours pré-rempli à chaque fois que vous ouvrirez l'outil et vous n'aurez qu'à éditer ce JSON par défaut selon vos besoins.

Enfin, passons à l'écriture du code Javascript.
Commencez par copier le code suivant dans la balise script du fichier HTML. Il s'agit du code de base qui associe les différentes actions aux boutons de l'interface.

var outputCode = ''; // Variable qui stocke le code généré.

var createClassCode = function($json)
{
	var classCode = '';
    
	// C'est ici qu'on va écrire le code qui interprète le JSON d'entrée et génère le code de la classe Java.

	return classCode;
};

// On attache au bouton "Create code" l'événement qui génère le code de la classe Java.
document.getElementById("create-code").onclick = function()
{
	// On récupère la chaîne de caractères contenue dans la champ d'entrée.
	var jsonStr = document.getElementById("input").value; 
    
	// On supprime les sauts de ligne et les tabulations de la chaîne de caractères.
	jsonStr = jsonStr.replaceAll('\n', '').replaceAll('\t', ''); 

	// On convertit la chaîne de caractères en objet JSON interprétable.
	var jsonData = {};

	try
	{
		jsonData = JSON.parse(jsonStr);
	}
	catch ($error)
	{
		jsonData = {"error":jsonStr};
	}

	// On génère le code de la classe Java.
   	outputCode = createClassCode(jsonData); 

	// On affiche le code généré dans le champ de sortie.
   	document.getElementById("code-block").innerHTML = outputCode; 
};

// On attache au bouton "Copy code" l'événement qui copie le code généré dans le presse papier.
document.getElementById("copy-code").onclick = function()
{
	if (!navigator.clipboard)
	{
		var textArea = document.createElement("textarea");
		textArea.value = outputCode;
						
		textArea.style.top = "-1000000000px";
		textArea.style.left = "-1000000000px";
		textArea.style.width = "100px";
		textArea.style.height = "100px";
		textArea.style.position = "absolute";
						
		document.body.appendChild(textArea);
		textArea.focus();
		textArea.select();
						
		try
		{
			var successful = document.execCommand("copy");
							
			if (successful)
			{
				console.log('Copié !');
			} 
			else
			{
				console.log('La copie a échoué.');
			} 
		}
		catch ($error) 
		{
			console.log('La copie a échoué.');
			console.log($error);
		} 
						
		document.body.removeChild(textArea);
	}
	else
	{
		navigator.clipboard.writeText(outputCode).then(function()
		{
			console.log('Copié !');
		}, 
		function($error) 
		{
			console.log('La copie a échoué.');
			console.log($error);
		});
	}
};

Maintenant, concentrons nous sur le code de la fonction createClassCode qui interprète le JSON d'entrée et génère le code de la classe Java.
Le code est expliqué dans les commentaires.

var createClassCode = function($json)
{
	var classCode = '';

	var attributesCode = ''; // Variable qui contient le code de déclaration des attributs.
	var methodsCode = ''; // Variable qui contient le code de déclaration des méthodes.
	var getCode = ''; // Variable qui contient le code des accesseurs GET.
	var setCode = ''; // Variable qui contient le code des accesseurs SET.

	// Création du code qui concerne les attributs.
	for (var i = 0; i < $json["attributes"].length; i++)
	{
		// Déclaration de l'attribut
		attributesCode = attributesCode + '\t' + $json["attributes"][i]["accessibility"];

		// Définir si c'est une constante ou non
   		if ($json["attributes"][i]["isConst"] === true)
		{
			attributesCode = attributesCode + ' final';
		}

		// Définir si c'est un attribut statique ou non
        if ($json["attributes"][i]["isStatic"] === true)
		{
			attributesCode = attributesCode + ' static';
		}

		attributesCode = attributesCode + ' ' + $json["attributes"][i]["type"]; // Type
		attributesCode = attributesCode + ' ' + $json["attributes"][i]["name"]; // Nom

		// Valeur par défaut
   		if ($json["attributes"][i]["default"] !== null)
		{
			// Traitement spécifique pour les chaînes de caractères
   			if ($json["attributes"][i]["type"] === "String")
			{
				attributesCode = attributesCode + ' = "' + $json["attributes"][i]["default"] + '"';
			}
			else
			{
				attributesCode = attributesCode + ' = ' + $json["attributes"][i]["default"];
			}
		}

		attributesCode = attributesCode + ';\n';

		// Déclaration de la méthode get correspondante
		getCode = getCode + '\tpublic';

		// La méthode est statique si l'attribut est statique
   		if ($json["attributes"][i]["isStatic"] === true)
		{
			getCode = getCode + ' static';
		}

		getCode = getCode + ' ' + $json["attributes"][i]["type"]; // On retourne le même type que l'attribut

		// Nom de la méthode, on commence par is si c'est un booléan et get sinon
   		if ($json["attributes"][i]["type"] === "boolean")
		{
			getCode = getCode + ' is';
		}
		else
		{
			getCode = getCode + ' get';
		}

		getCode = getCode + $json["attributes"][i]["name"].charAt(0).toUpperCase() + $json["attributes"][i]["name"].slice(1) + '() ';
        
		// Retourner la valeur de l'attribut
   		getCode = getCode + '{ return '

		if ($json["attributes"][i]["isStatic"] === true)
		{
			getCode = getCode + $json["className"] + '.' + $json["attributes"][i]["name"];
		}
		else
		{
			getCode = getCode + 'this.' + $json["attributes"][i]["name"];
		}

		getCode = getCode + '; }\n';

		// Déclaration de la méthode set correspondante
		if ($json["attributes"][i]["isConst"] !== true)
		{
			setCode = setCode + '\tpublic';

			// La méthode est statique si l'attribut est statique
   			if ($json["attributes"][i]["isStatic"] === true)
			{
				setCode = setCode + ' static';
			}

			setCode = setCode + ' void';
			setCode = setCode + ' set';
            
			// Paramètre d'entrée de la méthode
   			setCode = setCode + $json["attributes"][i]["name"].charAt(0).toUpperCase() + $json["attributes"][i]["name"].slice(1) + '(';
			setCode = setCode + $json["attributes"][i]["type"] + ' ';
			setCode = setCode + $json["attributes"][i]["name"];
			setCode = setCode + ') ';
			setCode = setCode + '{ '

			// Affecter une nouvelle valeur à l'attribut
   			if ($json["attributes"][i]["isStatic"] === true)
			{
				setCode = setCode + $json["className"] + '.' + $json["attributes"][i]["name"] + ' = ' + $json["attributes"][i]["name"];
			}
			else
			{
				setCode = setCode + 'this.' + $json["attributes"][i]["name"] + ' = ' + $json["attributes"][i]["name"];
			}

			setCode = setCode + '; }\n';
		}
	}

	// Création du code de déclaration des méthodes
	for (var i = 0; i < $json["methods"].length; i++)
	{
		methodsCode = methodsCode + '\t' + $json["methods"][i]["accessibility"];

		// Déclarer si la méthode est statique ou non
   		if ($json["methods"][i]["isStatic"] === true)
		{
			methodsCode = methodsCode + ' static';
		}

		// Type de retour
   		methodsCode = methodsCode  + ' ' + $json["methods"][i]["returnType"];
        
		// Nom
   		methodsCode = methodsCode + ' ' + $json["methods"][i]["name"] + '(';

		// Liste des paramètres
   		for (var j = 0; j < $json["methods"][i]["params"].length; j++)
		{
			if (j > 0)
			{
				methodsCode = methodsCode + ', ';
			}

			methodsCode = methodsCode + $json["methods"][i]["params"][j]["type"] + ' ';
			methodsCode = methodsCode + $json["methods"][i]["params"][j]["name"];
		}

		methodsCode = methodsCode + ')\n';
		methodsCode = methodsCode + '\t{\n'
		methodsCode = methodsCode + '\t\t// Do something...\n';

		// Instruction de retour en fonction du type attendu
   		if ($json["methods"][i]["returnType"] === 'int')
		{
			methodsCode = methodsCode + '\n\t\treturn 0;\n';
		}
		else if ($json["methods"][i]["returnType"] === 'float')
		{
			methodsCode = methodsCode + '\n\t\treturn 0.0f;\n';
		}
		else if ($json["methods"][i]["returnType"] === 'double')
		{
			methodsCode = methodsCode + '\n\t\treturn 0.0;\n';
		}
		else if ($json["methods"][i]["returnType"] === 'String')
		{
			methodsCode = methodsCode + '\n\t\treturn "";\n';
		}
		else if ($json["methods"][i]["returnType"] === 'boolean')
		{
			methodsCode = methodsCode + '\n\t\treturn true;\n';
		}

		methodsCode = methodsCode + '\t}\n\n';
	}

	// Création du code complet de la classe

	// Déclaration de la classe
	classCode = 'public class ' + $json["className"];

	// Ajout de l'héritage s'il y en a un
	if ($json["extends"] !== null)
	{
		classCode = classCode + ' extends ' + $json["extends"];
	}

	classCode = classCode + '\n{\n\n';

	// Insertion du code de déclaration des attributs
	classCode = classCode + '\t////////////////\n';
	classCode = classCode + '\t// Attributes //\n';
	classCode = classCode + '\t////////////////\n\n';

	classCode = classCode + attributesCode;

	// Insertion du code de déclaration des méthodes
	classCode = classCode + '\n\t/////////////\n';
	classCode = classCode + '\t// Methods //\n';
	classCode = classCode + '\t/////////////\n\n';

	classCode = classCode + methodsCode;

	// Insertion du code des accesseurs GET
	classCode = classCode + '\n\t/////////////////////\n';
	classCode = classCode + '\t// Getters/Setters //\n';
	classCode = classCode + '\t/////////////////////\n\n';

	classCode = classCode + '\t// GET\n\n';

	classCode = classCode + getCode;

	classCode = classCode + '\n\t// SET\n\n';

	classCode = classCode + setCode;

	classCode = classCode + '}';

	return classCode;
};

Voici le code complet de notre générateur.

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8" />
		<title>Java class generator</title>

		<!-- Le CSS qui structure l'interface -->
		<style>

			body
			{
				position: absolute;
				left: 0px;
				right: 0px;
				top: 0px;
				bottom: 0px;
			}

			#input, #code-block
			{
				position: absolute;
				top: 10px;
				bottom: 40px;
			}

			#input
			{
				left: 10px;
				right: calc(50% + 7px);
				resize: none;
			}

			#code-block
			{
				left: calc(50% + 7px);
				right: 10px;
				margin: 0px;
				padding: 10px;
				border: 1px solid black;
				overflow: auto;
			}

			#create-code-block, #copy-code-block
			{
				position: absolute;
				bottom: 0px;
				height: 30px;
				margin: 0px;
				text-align: center;
			}

			#create-code-block
			{
				left: 10px;
				right: calc(50% + 7px);
			}

			#copy-code-block
			{
				left: calc(50% + 7px);
				right: 10px;
			}

		</style>
	</head>

	<body>
		<!-- Champ de saisie du JSON d'entrée -->
		<textarea id="input" >
{
	"className": "Example",
	"extends": "MotherClass",
	"attributes":
	[
		{
			"name": "integerVariable",
			"type": "int",
			"default": 0,
			"accessibility": "private",
			"constructorParam": true,
			"isStatic": false,
			"isConst": false
		}
	],
	"methods":
	[
		{
			"name": "method1",
			"returnType": "int",
			"accessibility": "private",
			"isStatic": false,
			"params":
			[
				{ "name": "param1", "type": "int" },
				{ "name": "param2", "type": "float" },
				{ "name": "param3", "type": "double" },
				{ "name": "param4", "type": "String" },
				{ "name": "param5", "type": "boolean" }
			]
		}
	]
}
		</textarea>
		
		<!-- Bloc qui affiche le code généré -->
		<pre id="code-block" ></pre>
		
		<!-- Bouton qui déclenche la génération du code -->
		<p id="create-code-block" >
			<input id="create-code" type="button" value="Create code" />
		</p>

		<!-- Bouton qui déclenche la copy du code généré -->
		<p id="copy-code-block" >
			<input id="copy-code" type="button" value="Copy code" />
		</p>

		<script type="text/javascript" >

			var outputCode = ''; // Variable qui stocke le code généré.

			var createClassCode = function($json)
			{
				var classCode = '';

				var attributesCode = ''; // Variable qui contient le code de déclaration des attributs.
				var methodsCode = ''; // Variable qui contient le code de déclaration des méthodes.
				var getCode = ''; // Variable qui contient le code des accesseurs GET.
				var setCode = ''; // Variable qui contient le code des accesseurs SET.

				// Création du code qui concerne les attributs.
				for (var i = 0; i < $json["attributes"].length; i++)
				{
					// Déclaration de l'attribut
					attributesCode = attributesCode + '\t' + $json["attributes"][i]["accessibility"];

					// Définir si c'est une constante ou non
   					if ($json["attributes"][i]["isConst"] === true)
					{
						attributesCode = attributesCode + ' final';
					}

					// Définir si c'est un attribut statique ou non
   					if ($json["attributes"][i]["isStatic"] === true)
					{
						attributesCode = attributesCode + ' static';
					}

					attributesCode = attributesCode + ' ' + $json["attributes"][i]["type"]; // Type
					attributesCode = attributesCode + ' ' + $json["attributes"][i]["name"]; // Nom

					// Valeur par défaut
   					if ($json["attributes"][i]["default"] !== null)
					{
						// Traitement spécifique pour les chaînes de caractères
   						if ($json["attributes"][i]["type"] === "String")
						{
							attributesCode = attributesCode + ' = "' + $json["attributes"][i]["default"] + '"';
						}
						else
						{
							attributesCode = attributesCode + ' = ' + $json["attributes"][i]["default"];
						}
					}

					attributesCode = attributesCode + ';\n';

					// Déclaration de la méthode get correspondante
					getCode = getCode + '\tpublic';

					// La méthode est statique si l'attribut est statique
   					if ($json["attributes"][i]["isStatic"] === true)
					{
						getCode = getCode + ' static';
					}

					getCode = getCode + ' ' + $json["attributes"][i]["type"]; // On retourne le même type que l'attribut

					// Nom de la méthode, on commence par is si c'est un booléan et get sinon
   					if ($json["attributes"][i]["type"] === "boolean")
					{
						getCode = getCode + ' is';
					}
					else
					{
						getCode = getCode + ' get';
					}

					getCode = getCode + $json["attributes"][i]["name"].charAt(0).toUpperCase() + $json["attributes"][i]["name"].slice(1) + '() ';
                    
					// Retourner la valeur de l'attribut
   					getCode = getCode + '{ return '

					if ($json["attributes"][i]["isStatic"] === true)
					{
						getCode = getCode + $json["className"] + '.' + $json["attributes"][i]["name"];
					}
					else
					{
						getCode = getCode + 'this.' + $json["attributes"][i]["name"];
					}

					getCode = getCode + '; }\n';

					// Déclaration de la méthode set correspondante
					if ($json["attributes"][i]["isConst"] !== true)
					{
						setCode = setCode + '\tpublic';

						// La méthode est statique si l'attribut est statique
   						if ($json["attributes"][i]["isStatic"] === true)
						{
							setCode = setCode + ' static';
						}

						setCode = setCode + ' void';
						setCode = setCode + ' set';
                        
						// Paramètre d'entrée de la méthode
   						setCode = setCode + $json["attributes"][i]["name"].charAt(0).toUpperCase() + $json["attributes"][i]["name"].slice(1) + '(';
						setCode = setCode + $json["attributes"][i]["type"] + ' ';
						setCode = setCode + $json["attributes"][i]["name"];
						setCode = setCode + ') ';
						setCode = setCode + '{ '

						// Affecter une nouvelle valeur à l'attribut
   						if ($json["attributes"][i]["isStatic"] === true)
						{
							setCode = setCode + $json["className"] + '.' + $json["attributes"][i]["name"] + ' = ' + $json["attributes"][i]["name"];
						}
						else
						{
							setCode = setCode + 'this.' + $json["attributes"][i]["name"] + ' = ' + $json["attributes"][i]["name"];
						}

						setCode = setCode + '; }\n';
					}
				}

				// Création du code de déclaration des méthodes
				for (var i = 0; i < $json["methods"].length; i++)
				{
					methodsCode = methodsCode + '\t' + $json["methods"][i]["accessibility"];

					// Déclarer si la méthode est statique ou non
   					if ($json["methods"][i]["isStatic"] === true)
					{
						methodsCode = methodsCode + ' static';
					}

					// Type de retour
   					methodsCode = methodsCode  + ' ' + $json["methods"][i]["returnType"];
                    
					// Nom
   					methodsCode = methodsCode + ' ' + $json["methods"][i]["name"] + '(';

					// Liste des paramètres
   					for (var j = 0; j < $json["methods"][i]["params"].length; j++)
					{
						if (j > 0)
						{
							methodsCode = methodsCode + ', ';
						}

						methodsCode = methodsCode + $json["methods"][i]["params"][j]["type"] + ' '; // Type du paramètre
						methodsCode = methodsCode + $json["methods"][i]["params"][j]["name"]; // Nom du paramètre
					}

					methodsCode = methodsCode + ')\n';
					methodsCode = methodsCode + '\t{\n'
					methodsCode = methodsCode + '\t\t// Do something...\n';

					// Instruction de retour en fonction du type attendu
   					if ($json["methods"][i]["returnType"] === 'int')
					{
						methodsCode = methodsCode + '\n\t\treturn 0;\n';
					}
					else if ($json["methods"][i]["returnType"] === 'float')
					{
						methodsCode = methodsCode + '\n\t\treturn 0.0f;\n';
					}
					else if ($json["methods"][i]["returnType"] === 'double')
					{
						methodsCode = methodsCode + '\n\t\treturn 0.0;\n';
					}
					else if ($json["methods"][i]["returnType"] === 'String')
					{
						methodsCode = methodsCode + '\n\t\treturn "";\n';
					}
					else if ($json["methods"][i]["returnType"] === 'boolean')
					{
						methodsCode = methodsCode + '\n\t\treturn true;\n';
					}

					methodsCode = methodsCode + '\t}\n\n';
				}

				// Création du code complet de la classe

				// Déclaration de la classe
				classCode = 'public class ' + $json["className"];

				// Ajout de l'héritage s'il y en a un
				if ($json["extends"] !== null)
				{
					classCode = classCode + ' extends ' + $json["extends"];
				}

				classCode = classCode + '\n{\n\n';

				// Insertion du code de déclaration des attributs
				classCode = classCode + '\t////////////////\n';
				classCode = classCode + '\t// Attributes //\n';
				classCode = classCode + '\t////////////////\n\n';

				classCode = classCode + attributesCode;

				// Insertion du code de déclaration des méthodes
				classCode = classCode + '\n\t/////////////\n';
				classCode = classCode + '\t// Methods //\n';
				classCode = classCode + '\t/////////////\n\n';

				classCode = classCode + methodsCode;

				// Insertion du code des accesseurs GET
				classCode = classCode + '\n\t/////////////////////\n';
				classCode = classCode + '\t// Getters/Setters //\n';
				classCode = classCode + '\t/////////////////////\n\n';

				classCode = classCode + '\t// GET\n\n';

				classCode = classCode + getCode;

				classCode = classCode + '\n\t// SET\n\n';

				classCode = classCode + setCode;

				classCode = classCode + '}';

				return classCode;
			};

			// On attache au bouton "Create code" l'événement qui génère le code de la classe Java.
			document.getElementById("create-code").onclick = function()
			{
				// On récupère la chaîne de caractères contenue dans la champ d'entrée.
				var jsonStr = document.getElementById("input").value; 

				// On supprime les sauts de ligne et les tabulations de la chaîne de caractères.
				jsonStr = jsonStr.replaceAll('\n', '').replaceAll('\t', ''); 

				console.log(jsonStr);

				// On convertit la chaîne de caractères en objet JSON interprétable.
				var jsonData = {};

				try
				{
					jsonData = JSON.parse(jsonStr);
				}
				catch ($error)
				{
					jsonData = {"error":jsonStr};
				}

				console.log(jsonData);

				// On génère le code de la classe Java.
				outputCode = createClassCode(jsonData); 

				// On affiche le code généré dans le champ de sortie.
				document.getElementById("code-block").innerHTML = outputCode; 
			};

			// On attache au bouton "Copy code" l'événement qui copie le code généré dans le presse papier.
			document.getElementById("copy-code").onclick = function()
			{
				if (!navigator.clipboard)
				{
					var textArea = document.createElement("textarea");
					textArea.value = outputCode;
						
					textArea.style.top = "-1000000000px";
					textArea.style.left = "-1000000000px";
					textArea.style.width = "100px";
					textArea.style.height = "100px";
					textArea.style.position = "absolute";
						
					document.body.appendChild(textArea);
					textArea.focus();
					textArea.select();
						
					try
					{
						var successful = document.execCommand("copy");
							
						if (successful)
						{
							console.log('Copié !');
						} 
						else
						{
							console.log('La copie a échoué.');
						} 
					}
					catch ($error) 
					{
						console.log('La copie a échoué.');
						console.log($error);
					} 
						
					document.body.removeChild(textArea);
				}
				else
				{
					navigator.clipboard.writeText(outputCode).then(function()
					{
						console.log('Copié !');
					}, 
					function($error) 
					{
						console.log('La copie a échoué.');
						console.log($error);
					});
				}
			};

		</script>
	</body>
</html>

Voilà ! Vous avez un générateur de classe Java !
Si vous le testez avec le JSON d'exemple que nous avons créé ensemble, vous obtiendrez ce code là, avec des méthodes vides qu'il vous faudra remplir à la main.

public class Example extends MotherClass
{

	////////////////
	// Attributes //
	////////////////

	private int integerVariable = 0;

	/////////////
	// Methods //
	/////////////

	private int method1(int param1, float param2, double param3, String param4, boolean param5)
	{
		// Do something...

		return 0;
	}


	/////////////////////
	// Getters/Setters //
	/////////////////////

	// GET

	public int getIntegerVariable() { return this.integerVariable; }

	// SET

	public void setIntegerVariable(int integerVariable) { this.integerVariable = integerVariable; }
}

Une fois la structure de la classe générée, vous n'avez plus qu'à vous concentrer sur la partie spécifique du code, donc la plus intéressante. Mais ça aussi ça peut être accéléré sans IA. Nous verrons ça plus loin.

☝️

Ceci est un exemple d'outil. Je vous recommande de modifier le code pour l'adapter au langage de votre choix et à vos besoins spécifiques.

Par exemple, j'ajoute souvent une méthode d'export des valeurs des attributs au format JSON, ainsi qu'une méthode qui renseigne les attributs à partir d'un JSON.

Vous pouvez aussi générer ainsi des champs de saisie associés aux attributs et générer les interactions et les contrôles qui vont avec lorsque vous développez une interface graphique.

Vous pouvez aussi ajouter des types et les traitements qui leurs correspondent si des classes de votre création ou non sont utilisées très couramment dans votre projet. Dans le cas présent, je n'ai couvert que les types int, float, double, boolean et String.

Vous pouvez aussi générer des tests unitaires.

Bref ! Tout ce qui se répète dans l'architecture de votre projet, vous le mettez ici. Vous n'avez de limite que votre imagination ! 😃

Maintenant on va pousser le principe plus loin en développant un outil qui génère la structure d'un projet complet !

Générer la structure d'un projet entier

On va toujours utiliser Javascript pour écrire notre nouvel outil de génération de code mais on ne l'exécutera plus dans un navigateur, mais avec Node.js, dans une console (DOS ou UNIX).
Pourquoi ?
Parce que ce nouvel outil va écrire une arborescence de fichiers sur votre disque dur, ce qu'un navigateur ne peut pas faire.

Donc, pour commencer, il va vous falloir installer Node.js.

Cela fait, créez un répertoire qui contiendra votre générateur de projet. Je vais l'appeler JavaProjectGenerator.

Capture du Finder montrant le répertoire JavaProjectGenerator vide.

Maintenant, nous allons y écrire notre script, ainsi qu'un fichier de paramétrage qui servira d'exemple. Celui-ci contiendra un JSON du même genre que le précédent qui pilotera le script de génération de code.

Commençons justement par le fichier de paramétrage.

Le JSON de paramétrage comporte 3 éléments : le nom du projet, le répertoire ou le projet sera enregistré et la liste des classes.

{
    "projectName": "GeneratorTest",
    "projectPath": "/Chemin/du/repertoire/contenant/le/projet/",
    "classes": []
}

Il ne reste plus qu'à ajouter la configuration de chaque classe dans le tableau classes.
On conserve le même schéma de configuration pour chaque classe que ce que l'on a utilisé pour l'outil qui génère une classe unique. On y ajoute juste quelques informations supplémentaires : isMain pour indiquer s'il s'agit du point d'entrée du projet et path pour indiquer le répertoire où doit être enregistrée la classe (le chemin est relatif au répertoire du projet).

{
    "projectName": "GeneratorTest",
    "projectPath": "/Chemin/du/repertoire/contenant/le/projet/",
    "classes":
    [
        {
            "className": "Main",
            "path": "test",
            "isMain": true,
            "extends": null,
            "attributes": [],
            "methods": []
        },
        {
            "className": "Example1",
            "path": "test/dir1",
            "isMain": false,
            "extends": null,
            "attributes":
            [
                {
                    "name": "integerVariable",
                    "type": "int",
                    "default": 0,
                    "accessibility": "private",
                    "constructorParam": true,
                    "isStatic": false,
                    "isConst": false
                }
            ],
            "methods":
            [
                {
                    "name": "method1",
                    "returnType": "int",
                    "accessibility": "protected",
                    "isStatic": false,
                    "params":
                    [
                        { "name": "param1", "type": "int" },
                        { "name": "param2", "type": "float" },
                        { "name": "param3", "type": "double" },
                        { "name": "param4", "type": "String" },
                        { "name": "param5", "type": "boolean" }
                    ]
                }
            ]
        },
        {
            "className": "Example2",
            "path": "test/dir2",
            "isMain": false,
            "extends": "Example1",
            "attributes":
            [
                {
                    "name": "integerVariable",
                    "type": "int",
                    "default": 0,
                    "accessibility": "private",
                    "constructorParam": true,
                    "isStatic": false,
                    "isConst": false
                }
            ],
            "methods":
            [
                {
                    "name": "method2",
                    "returnType": "int",
                    "accessibility": "public",
                    "isStatic": false,
                    "params":
                    [
                        { "name": "param1", "type": "int" },
                        { "name": "param2", "type": "float" },
                        { "name": "param3", "type": "double" },
                        { "name": "param4", "type": "String" },
                        { "name": "param5", "type": "boolean" }
                    ]
                }
            ]
        }
    ]
}

Maintenant que vous avez votre exemple de JSON de paramétrage, copiez-le et enregistrez le dans un fichier que vous pouvez appeler param.json. Conservez le précieusement, comme ça vous n'aurez qu'à l'éditer pour chaque nouveau projet au lieu d'en réécrire depuis zéro à chaque fois.

Maintenant, on va pouvoir passer au script. Il se compose essentiellement de la fonction qui génère le code d'une classe avec quelques ajouts.
Les explications sont dans les commentaires du code ci-dessous.

// Chargement du module fs pour écrire les fichiers
const fs = require('fs');

// Arguments passés via la ligne de commande
const arguments = process.argv

// Lecture du fichier de paramétrage
var jsonStr = fs.readFileSync(arguments[2], "utf8");
jsonData = JSON.parse(jsonStr);

// Dictionnaire des classes (pour accéder au paramètres d'une classe à partir de son nom sans avoir à parcourir un tableau à chaque fois)
var dictClasses = {};

for (var i = 0; i < jsonData["classes"].length; i++)
{
    dictClasses[jsonData["classes"][i]["className"]] = jsonData["classes"][i];
}

// On reprend la fonction de génération du code d'une classe avec quelques modifications
var createClassCode = function($json)
{
    var classCode = '';

	var attributesCode = ''; // Variable qui contient le code de déclaration des attributs.
	var methodsCode = ''; // Variable qui contient le code de déclaration des méthodes.
	var getCode = ''; // Variable qui contient le code des accesseurs GET.
	var setCode = ''; // Variable qui contient le code des accesseurs SET.

    // Création du code qui concerne les attributs.
    for (var i = 0; i < $json["attributes"].length; i++)
	{
		// Déclaration de l'attribut
		attributesCode = attributesCode + '\t' + $json["attributes"][i]["accessibility"]; // Accessibilité (private, protected, public)

        // Définir si c'est une constante ou non
		if ($json["attributes"][i]["isConst"] === true)
		{
			attributesCode = attributesCode + ' final';
		}

        // Définir si c'est un attribut statique ou non
		if ($json["attributes"][i]["isStatic"] === true)
		{
			attributesCode = attributesCode + ' static';
		}

		attributesCode = attributesCode + ' ' + $json["attributes"][i]["type"]; // Type
		attributesCode = attributesCode + ' ' + $json["attributes"][i]["name"]; // Nom

        // Valeur par défaut
		if ($json["attributes"][i]["default"] !== null)
		{
            // Traitement spécifique pour les chaînes de caractères
			if ($json["attributes"][i]["type"] === "String")
			{
				attributesCode = attributesCode + ' = "' + $json["attributes"][i]["default"] + '"';
			}
			else
			{
				attributesCode = attributesCode + ' = ' + $json["attributes"][i]["default"];
			}
		}

		attributesCode = attributesCode + ';\n';

		// Déclaration de la méthode get correspondante
		getCode = getCode + '\tpublic';

        // La méthode est statique si l'attribut est statique
		if ($json["attributes"][i]["isStatic"] === true)
		{
			getCode = getCode + ' static';
		}

		getCode = getCode + ' ' + $json["attributes"][i]["type"]; // On retourne le même type que l'attribut

        // Nom de la méthode, on commence par is si c'est un booléan et get sinon
		if ($json["attributes"][i]["type"] === "boolean")
		{
			getCode = getCode + ' is';
		}
		else
		{
			getCode = getCode + ' get';
		}

		getCode = getCode + $json["attributes"][i]["name"].charAt(0).toUpperCase() + $json["attributes"][i]["name"].slice(1) + '() ';
        
        // Retourner la valeur de l'attribut
		getCode = getCode + '{ return '

		if ($json["attributes"][i]["isStatic"] === true)
		{
			getCode = getCode + $json["className"] + '.' + $json["attributes"][i]["name"];
		}
		else
		{
			getCode = getCode + 'this.' + $json["attributes"][i]["name"];
		}

		getCode = getCode + '; }\n';

		// Déclaration de la méthode set correspondante
		if ($json["attributes"][i]["isConst"] !== true)
		{
			setCode = setCode + '\tpublic';

            // La méthode est statique si l'attribut est statique
			if ($json["attributes"][i]["isStatic"] === true)
			{
				setCode = setCode + ' static';
			}

			setCode = setCode + ' void';
			setCode = setCode + ' set';
            
            // Paramètre d'entrée de la méthode
			setCode = setCode + $json["attributes"][i]["name"].charAt(0).toUpperCase() + $json["attributes"][i]["name"].slice(1) + '(';
			setCode = setCode + $json["attributes"][i]["type"] + ' ';
			setCode = setCode + $json["attributes"][i]["name"];
			setCode = setCode + ') ';
			setCode = setCode + '{ '

            // Affecter une nouvelle valeur à l'attribut
			if ($json["attributes"][i]["isStatic"] === true)
			{
				setCode = setCode + $json["className"] + '.' + $json["attributes"][i]["name"] + ' = ' + $json["attributes"][i]["name"];
			}
			else
			{
				setCode = setCode + 'this.' + $json["attributes"][i]["name"] + ' = ' + $json["attributes"][i]["name"];
			}

			setCode = setCode + '; }\n';
		}
	}

	// Création du code de déclaration des méthodes
	for (var i = 0; i < $json["methods"].length; i++)
	{
		methodsCode = methodsCode + '\t' + $json["methods"][i]["accessibility"]; // Accessibilité (private, protected, public)

        // Déclarer si la méthode est statique ou non
		if ($json["methods"][i]["isStatic"] === true)
		{
			methodsCode = methodsCode + ' static';
		}

        // Type de retour
		methodsCode = methodsCode  + ' ' + $json["methods"][i]["returnType"];
        
        // Nom
		methodsCode = methodsCode + ' ' + $json["methods"][i]["name"] + '(';

        // Liste des paramètres
		for (var j = 0; j < $json["methods"][i]["params"].length; j++)
		{
			if (j > 0)
			{
				methodsCode = methodsCode + ', ';
			}

			methodsCode = methodsCode + $json["methods"][i]["params"][j]["type"] + ' '; // Type du paramètre
			methodsCode = methodsCode + $json["methods"][i]["params"][j]["name"]; // Nom du paramètre
		}

		methodsCode = methodsCode + ')\n';
		methodsCode = methodsCode + '\t{\n'
		methodsCode = methodsCode + '\t\t// Do something...\n';

        // Instruction de retour en fonction du type attendu
		if ($json["methods"][i]["returnType"] === 'int')
		{
			methodsCode = methodsCode + '\n\t\treturn 0;\n';
		}
		else if ($json["methods"][i]["returnType"] === 'float')
		{
			methodsCode = methodsCode + '\n\t\treturn 0.0f;\n';
		}
		else if ($json["methods"][i]["returnType"] === 'double')
		{
			methodsCode = methodsCode + '\n\t\treturn 0.0;\n';
		}
		else if ($json["methods"][i]["returnType"] === 'String')
		{
			methodsCode = methodsCode + '\n\t\treturn "";\n';
		}
		else if ($json["methods"][i]["returnType"] === 'boolean')
		{
			methodsCode = methodsCode + '\n\t\treturn true;\n';
		}

		methodsCode = methodsCode + '\t}\n\n';
	}

    // Création du code complet de la classe
    
    // Import du package
    classCode = classCode + 'package ' + $json["path"].replaceAll('/', '.') + ';\n\n';
    
    // Import de la classe mère s'il y a un héritage
    if ($json["extends"] !== null && dictClasses[$json["extends"]] !== undefined && dictClasses[$json["extends"]] !== null)
    {
        classCode = classCode + 'import ' + dictClasses[$json["extends"]]["path"].replaceAll('/', '.') + '.' + $json["extends"] + ';\n\n';
    }

	// Déclaration de la classe
	classCode = classCode + 'public class ' + $json["className"];

	// Ajout de l'héritage s'il y en a un
	if ($json["extends"] !== null)
	{
		classCode = classCode + ' extends ' + $json["extends"];
	}

	classCode = classCode + '\n{\n\n';

	// Insertion du code de déclaration des attributs
	classCode = classCode + '\t////////////////\n';
	classCode = classCode + '\t// Attributes //\n';
	classCode = classCode + '\t////////////////\n\n';

	classCode = classCode + attributesCode;

	// Insertion du code de déclaration des méthodes
	classCode = classCode + '\n\t/////////////\n';
	classCode = classCode + '\t// Methods //\n';
	classCode = classCode + '\t/////////////\n\n';
    
    // Ajout de la méthode main pour le point d'entrée
    if ($json["isMain"] === true)
    {
        classCode = classCode + '\tpublic static void main(String[] args)\n';
        classCode = classCode + '\t{\n';
        classCode = classCode + '\t\t// Do something...\n';
        classCode = classCode + '\t}\n\n';
    }

	classCode = classCode + methodsCode;

	// Insertion du code des accesseurs GET
	classCode = classCode + '\n\t/////////////////////\n';
	classCode = classCode + '\t// Getters/Setters //\n';
	classCode = classCode + '\t/////////////////////\n\n';

	classCode = classCode + '\t// GET\n\n';

	classCode = classCode + getCode;

	classCode = classCode + '\n\t// SET\n\n';

	classCode = classCode + setCode;

	classCode = classCode + '}';

	return classCode;
};

// Création du répertoire du projet
if (!fs.existsSync(jsonData["projectPath"] + jsonData["projectName"]))
	fs.mkdirSync(jsonData["projectPath"] + jsonData["projectName"]);

// Création du répertoire du code source du projet
if (!fs.existsSync(jsonData["projectPath"] + jsonData["projectName"] + '/src'))
	fs.mkdirSync(jsonData["projectPath"] + jsonData["projectName"] + '/src');

// Création des répertoires et des fichiers du code source
for (var i = 0; i < jsonData["classes"].length; i++)
{
    if (!fs.existsSync(jsonData["projectPath"] + jsonData["projectName"] + '/src/' + jsonData["classes"][i]["path"]))
	   fs.mkdirSync(jsonData["projectPath"] + jsonData["projectName"] + '/src/' + jsonData["classes"][i]["path"]);
    
    var classCode = createClassCode(jsonData["classes"][i]);
    
    fs.writeFileSync(jsonData["projectPath"] + jsonData["projectName"] + '/src/' + jsonData["classes"][i]["path"] + '/' + jsonData["classes"][i]["className"] + '.java', classCode);
}

Maintenant, j'ai 2 fichiers dans mon répertoire JavaProjectGenerator.

Ensuite, pour exécuter votre script, ouvrez une console DOS ou UNIX et positionnez vous dans le répertoire nouvellement créé avec la commande cd.
Puis entrez la commande suivante.

node index.js param.json

Et voilà ! Votre nouvel outil vous génère la structure complète d'un projet en un claquement de doigts ! 😄

⚠️

Par contre, cet outil là vous oblige à bien réfléchir à l'architecture de votre projet avant de l'utiliser.
Ceci dit, rien n'est figé dans le marbre. Vous pouvez toujours utiliser le premier outil qui génère le code d'une classe unique si vous avez besoin d'en ajouter de nouvelles après coup dans votre projet.

☝️

Si vous êtes encore peu expérimentés en code, je vous recommande au passage de vous former sur la notion de design pattern pour vous apprendre à bien réfléchir et structurer vos projets. Ça rendra l'automatisation de l'écriture du code d'autant plus facile.

Bon ! Maintenant on va refaire la même chose pour le code SQL d'une base de données !

Générer le code SQL d'une base de données

Code SQL pour une seule table

Pour commencer, on va créer un premier outil pour générer le code SQL relatif à une table unique.
Pour cela, on va reprendre la même interface HTML que pour le générateur de classe Java. Seul le titre dans la balise title change.

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8" />
		<title>SQL generator</title>

		<!-- Le CSS qui structure l'interface -->
		<style>

			body
			{
				position: absolute;
				left: 0px;
				right: 0px;
				top: 0px;
				bottom: 0px;
			}

			#input, #code-block
			{
				position: absolute;
				top: 10px;
				bottom: 40px;
			}

			#input
			{
				left: 10px;
				right: calc(50% + 7px);
				resize: none;
			}

			#code-block
			{
				left: calc(50% + 7px);
				right: 10px;
				margin: 0px;
				padding: 10px;
				border: 1px solid black;
				overflow: auto;
			}

			#create-code-block, #copy-code-block
			{
				position: absolute;
				bottom: 0px;
				height: 30px;
				margin: 0px;
				text-align: center;
			}

			#create-code-block
			{
				left: 10px;
				right: calc(50% + 7px);
			}

			#copy-code-block
			{
				left: calc(50% + 7px);
				right: 10px;
			}

		</style>
	</head>

	<body>
		<!-- Champ de saisie du JSON d'entrée -->
		<textarea id="input" >

			<!-- On va écrire ici un exemple de JSON attendu -->

		</textarea>
		
		<!-- Bloc qui affiche le code généré -->
		<pre id="code-block" ></pre>
		
		<!-- Bouton qui déclenche la génération du code -->
		<p id="create-code-block" >
			<input id="create-code" type="button" value="Create code" />
		</p>

		<!-- Bouton qui déclenche la copy du code généré -->
		<p id="copy-code-block" >
			<input id="copy-code" type="button" value="Copy code" />
		</p>

		<script type="text/javascript" >

			// C'est ici que l'on va écrire le Javascript qui génère le code.

		</script>
	</body>
</html>

Maintenant on passe au JSON de paramétrage.
Le paramètre tableName contient le nom de la table, le paramètre columns contient la liste des colonnes de la table et le paramètre constraints contient la liste des contraintes de la table (clé primaire, clé étrangère).
Pour chaque colonne, il faut renseigner son nom, son type, si elle accepte la valeur nulle ou non et sa valeur par défaut s'il y en a une.
Pour chaque contrainte, il faut renseigner son nom, son type (pk pour une clé primaire, fk pour une clé étrangère), la table de référence dans le cas des clés étrangères et la colonne de référence.

{
	"databaseName": "myDatabase",
    "tableName": "example",
	"columns":
	[
		{ "name": "id", "type": "INTEGER", "null": false, "default": "0" },
		{ "name": "textColumn", "type": "TEXT", "null": false, "default": "" },
		{ "name": "tinyintColumn", "type": "TINYINT", "null": false, "default": "0" },
		{ "name": "id_truc", "type": "INTEGER", "null": false, "default": "0" },
		{ "name": "floatColumn", "type": "FLOAT", "null": false, "default": "0.0" },
		{ "name": "datetimeColumn", "type": "DATETIME(6)", "null": true, "default": null }
	],
	"constraints":
	[
		{ "name": "pk_example", "type": "pk", "column": "id", "referenceColumn": "id" },
		{ "name": "fk_example_truc", "type": "fk", "column": "id_truc", "referenceTable": "truc", "referenceColumn": "id" }
	]
}

Maintenant que vous avez un exemple de JSON de paramétrage, copiez le dans la balise textarea, comme ça vous n'aurez plus qu'à éditer ce JSON quand vous ouvrirez l'outil plutôt qu'en écrire un depuis zéro.
Enfin, passons à l'écriture du code Javascript.
Commencez par copier le code suivant dans la balise script du fichier HTML. C'est le même code de base qui associe les différentes actions aux boutons de l'interface que pour l'outil de génération de la classe Java. J'ai juste changé le nom de la fonction qui génère le code. Elle s'appelle createSQLcode désormais.

var outputCode = ''; // Variable qui stocke le code généré.

var createSQLcode = function($json)
{
	var sqlCode = '';
    
	// C'est ici qu'on va écrire le code qui interprète le JSON d'entrée et génère le code SQL.

	return sqlCode;
};

// On attache au bouton "Create code" l'événement qui génère le code de la classe Java.
document.getElementById("create-code").onclick = function()
{
	// On récupère la chaîne de caractères contenue dans la champ d'entrée.
	var jsonStr = document.getElementById("input").value; 
    
	// On supprime les sauts de ligne et les tabulations de la chaîne de caractères.
	jsonStr = jsonStr.replaceAll('\n', '').replaceAll('\t', ''); 

	// On convertit la chaîne de caractères en objet JSON interprétable.
	var jsonData = {};

	try
	{
		jsonData = JSON.parse(jsonStr);
	}
	catch ($error)
	{
		jsonData = {"error":jsonStr};
	}

	// On génère le code SQL.
   	outputCode = createSQLcode(jsonData); 

	// On affiche le code généré dans le champ de sortie.
   	document.getElementById("code-block").innerHTML = outputCode; 
};

// On attache au bouton "Copy code" l'événement qui copie le code généré dans le presse papier.
document.getElementById("copy-code").onclick = function()
{
	if (!navigator.clipboard)
	{
		var textArea = document.createElement("textarea");
		textArea.value = outputCode;
						
		textArea.style.top = "-1000000000px";
		textArea.style.left = "-1000000000px";
		textArea.style.width = "100px";
		textArea.style.height = "100px";
		textArea.style.position = "absolute";
						
		document.body.appendChild(textArea);
		textArea.focus();
		textArea.select();
						
		try
		{
			var successful = document.execCommand("copy");
							
			if (successful)
			{
				console.log('Copié !');
			} 
			else
			{
				console.log('La copie a échoué.');
			} 
		}
		catch ($error) 
		{
			console.log('La copie a échoué.');
			console.log($error);
		} 
						
		document.body.removeChild(textArea);
	}
	else
	{
		navigator.clipboard.writeText(outputCode).then(function()
		{
			console.log('Copié !');
		}, 
		function($error) 
		{
			console.log('La copie a échoué.');
			console.log($error);
		});
	}
};

Et maintenant, on va pouvoir se concentrer sur le code de la fonction createSQLcode.
Les explications se trouvent dans les commentaires du code.

var createSQLcode = function($json)
{
	var createTable = ""; // Code de la requête de création de table
	var selectQuery = "SELECT " + $json['tableName'] + ".id AS id"; // Code de la requête de lecture
	var insertColumns = 'INSERT INTO ' + $json['databaseName'] + '.' + $json['tableName'] + '('; // Code de la requête d'insertion (colonnes)
	var insertValues = 'VALUES ('; // Code de la requête d'insertion (valeurs)
	var updateQuery = 'UPDATE ' + $json['databaseName'] + '.' + $json['tableName'] + '\nSET '; // Code de la requête de mise à jour
	var deleteQuery = 'DELETE FROM ' + $json['databaseName'] + '.' + $json['tableName'] + ' WHERE id = :id;\n'; // Code de la requête de suppression de ligne

	for (var i = 0; i < $json['columns'].length; i++)
	{
		// Création de table
		createTable = createTable + ',\n';
		createTable = createTable + $json['columns'][i]['name'] + ' ' + $json['columns'][i]['type'];

		if ($json['columns'][i]['default'] !== null)
		{
			if ($json['columns'][i]['type'] === 'TEXT' || $json['columns'][i]['type'] === 'DATETIME')
			{
				createTable = createTable + ' DEFAULT "' + $json['columns'][i]['default'] + '"';
			}
			else
			{
				createTable = createTable + ' DEFAULT ' + $json['columns'][i]['default'];
			}
		}

		if ($json['columns'][i]['null'] === false)
		{
			createTable = createTable + ' NOT NULL';
		}

		// Requête de sélection
		selectQuery = selectQuery + ',\n	';
		selectQuery = selectQuery + $json['tableName'] + "." + $json['columns'][i]['name'] + " AS " + $json['columns'][i]['name'];

		// Requête d'insertion
		if (i > 0)
		{
			insertColumns = insertColumns + ',\n	';
			insertValues = insertValues + ',\n	';
		}

		insertColumns = insertColumns + $json['columns'][i]['name'];
		insertValues = insertValues + ':' + $json['columns'][i]['name'];

		// Requête de mise à jour
		if (i > 0)
		{
			updateQuery = updateQuery + ',\n	';
		}

		updateQuery = updateQuery + $json['columns'][i]['name'] + ' = :' + $json['columns'][i]['name'];
	}

	selectQuery = selectQuery + '\nFROM ' + $json['databaseName'] + '.' + $json['tableName'] + ' ' + $json['tableName'] + ';\n\n';
	insertColumns = insertColumns + ')\n';
	insertValues = insertValues + ');\n\n';
	updateQuery = updateQuery + '\nWHERE id = :id;\n\n';

	// Contraintes
	for (var i = 0; i < $json['constraints'].length; i++)
	{
		createTable = createTable + ',\n';
		createTable = createTable + 'CONSTRAINT ' + $json['constraints'][i]['name'];

		if ($json['constraints'][i]['type'] === 'pk')
		{
			createTable = createTable + ' PRIMARY KEY (' + $json['constraints'][i]['referenceColumn'] + ')';
		}
		else
		{
			createTable = createTable + ' FOREIGN KEY (' + $json['constraints'][i]['column'] + ') REFERENCES ' + $json['constraints'][i]['referenceTable'] + '(' + $json['constraints'][i]['referenceColumn'] + ')';
		}
	}

	// Code SQL complet
	var sqlCode = 'DROP TABLE IF EXISTS ' + $json['databaseName'] + '.' + $json['tableName'] + ' RESTRICT;\n\n';

	sqlCode = sqlCode + '/* Create table query */\n';
	sqlCode = sqlCode + 'CREATE TABLE IF NOT EXISTS ' + $json['databaseName'] + '.' + $json['tableName'] + '\n';
	sqlCode = sqlCode + '(id INTEGER AUTO_INCREMENT';

	sqlCode = sqlCode + createTable;

	sqlCode = sqlCode + ')\n';
	sqlCode = sqlCode + 'ENGINE = InnoDB;\n\n';

	sqlCode = sqlCode + '/* Select query */\n';
	sqlCode = sqlCode + selectQuery;
	sqlCode = sqlCode + '/* Insert query */\n';
	sqlCode = sqlCode + insertColumns;
	sqlCode = sqlCode + insertValues;
	sqlCode = sqlCode + '/* Update query */\n';
	sqlCode = sqlCode + updateQuery;
	sqlCode = sqlCode + '/* Delete query */\n';
	sqlCode = sqlCode + deleteQuery;

	return sqlCode;
};

Vou noterez que je n'ai pris en charge que 6 types : INTEGRE, TEXT, TINYINT, FLOAT, DATETIME.
Il y en a de très nombreux autres. Je me suis contentée de prendre en charge certains des plus courants. Ce sont, personnellement, ceux que j'utilise le plus.
Notez aussi que pour l'insertion et la mise à jour des valeurs, j'ai utilisé la syntaxe de PDO en PHP, le double point précédent le nom d'une variable à la place d'une valeur. L'autre synthaxe courante avec d'autres bibliothèques d'intéractions avec les bases de données est le point d'intérogation (?).

Voici le code complet de l'outil que vous pouvez copier dans un fichier html.

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8" />
		<title>SQL generator</title>

		<!-- Le CSS qui structure l'interface -->
		<style>

			body
			{
				position: absolute;
				left: 0px;
				right: 0px;
				top: 0px;
				bottom: 0px;
			}

			#input, #code-block
			{
				position: absolute;
				top: 10px;
				bottom: 40px;
			}

			#input
			{
				left: 10px;
				right: calc(50% + 7px);
				resize: none;
			}

			#code-block
			{
				left: calc(50% + 7px);
				right: 10px;
				margin: 0px;
				padding: 10px;
				border: 1px solid black;
				overflow: auto;
			}

			#create-code-block, #copy-code-block
			{
				position: absolute;
				bottom: 0px;
				height: 30px;
				margin: 0px;
				text-align: center;
			}

			#create-code-block
			{
				left: 10px;
				right: calc(50% + 7px);
			}

			#copy-code-block
			{
				left: calc(50% + 7px);
				right: 10px;
			}

		</style>
	</head>

	<body>
		<!-- Champ de saisie du JSON d'entrée -->
		<textarea id="input" >
{
	"databaseName": "myDatabase",
	"tableName": "example",
	"columns":
	[
		{ "name": "id", "type": "INTEGER", "null": false, "default": "0" },
		{ "name": "textColumn", "type": "TEXT", "null": false, "default": "" },
		{ "name": "tinyintColumn", "type": "TINYINT", "null": false, "default": "0" },
		{ "name": "id_truc", "type": "INTEGER", "null": false, "default": "0" },
		{ "name": "floatColumn", "type": "FLOAT", "null": false, "default": "0.0" },
		{ "name": "datetimeColumn", "type": "DATETIME(6)", "null": true, "default": null }
	],
	"constraints":
	[
		{ "name": "pk_example", "type": "pk", "column": "id", "referenceColumn": "id" },
		{ "name": "fk_example_truc", "type": "fk", "column": "id_truc", "referenceTable": "truc", "referenceColumn": "id" }
	]
}
		</textarea>
		
		<!-- Bloc qui affiche le code généré -->
		<pre id="code-block" ></pre>
		
		<!-- Bouton qui déclenche la génération du code -->
		<p id="create-code-block" >
			<input id="create-code" type="button" value="Create code" />
		</p>

		<!-- Bouton qui déclenche la copy du code généré -->
		<p id="copy-code-block" >
			<input id="copy-code" type="button" value="Copy code" />
		</p>

		<script type="text/javascript" >
			var outputCode = ''; // Variable qui stocke le code généré.

			var createSQLcode = function($json)
			{
				var createTable = ""; // Code de la requête de création de table
				var selectQuery = "SELECT " + $json['tableName'] + ".id AS id"; // Code de la requête de lecture
				var insertColumns = 'INSERT INTO ' + $json['databaseName'] + '.' + $json['tableName'] + '('; // Code de la requête d'insertion (colonnes)
				var insertValues = 'VALUES ('; // Code de la requête d'insertion (valeurs)
				var updateQuery = 'UPDATE ' + $json['databaseName'] + '.' + $json['tableName'] + '\nSET '; // Code de la requête de mise à jour
				var deleteQuery = 'DELETE FROM ' + $json['databaseName'] + '.' + $json['tableName'] + ' WHERE id = :id;\n'; // Code de la requête de suppression de ligne

				for (var i = 0; i < $json['columns'].length; i++)
				{
					// Création de table
					createTable = createTable + ',\n';
					createTable = createTable + $json['columns'][i]['name'] + ' ' + $json['columns'][i]['type'];

					if ($json['columns'][i]['default'] !== null)
					{
						if ($json['columns'][i]['type'] === 'TEXT' || $json['columns'][i]['type'] === 'DATETIME')
						{
							createTable = createTable + ' DEFAULT "' + $json['columns'][i]['default'] + '"';
						}
						else
						{
							createTable = createTable + ' DEFAULT ' + $json['columns'][i]['default'];
						}
					}

					if ($json['columns'][i]['null'] === false)
					{
						createTable = createTable + ' NOT NULL';
					}

					// Requête de sélection
					selectQuery = selectQuery + ',\n	';
					selectQuery = selectQuery + $json['tableName'] + "." + $json['columns'][i]['name'] + " AS " + $json['columns'][i]['name'];

					// Requête d'insertion
					if (i > 0)
					{
						insertColumns = insertColumns + ',\n	';
						insertValues = insertValues + ',\n	';
					}

					insertColumns = insertColumns + $json['columns'][i]['name'];
					insertValues = insertValues + ':' + $json['columns'][i]['name'];

					// Requête de mise à jour
					if (i > 0)
					{
						updateQuery = updateQuery + ',\n	';
					}

					updateQuery = updateQuery + $json['columns'][i]['name'] + ' = :' + $json['columns'][i]['name'];
				}

				selectQuery = selectQuery + '\nFROM ' + $json['databaseName'] + '.' + $json['tableName'] + ' ' + $json['tableName'] + ';\n\n';
				insertColumns = insertColumns + ')\n';
				insertValues = insertValues + ');\n\n';
				updateQuery = updateQuery + '\nWHERE id = :id;\n\n';

				// Contraintes
				for (var i = 0; i < $json['constraints'].length; i++)
				{
					createTable = createTable + ',\n';
					createTable = createTable + 'CONSTRAINT ' + $json['constraints'][i]['name'];

					if ($json['constraints'][i]['type'] === 'pk')
					{
						createTable = createTable + ' PRIMARY KEY (' + $json['constraints'][i]['referenceColumn'] + ')';
					}
					else
					{
						createTable = createTable + ' FOREIGN KEY (' + $json['constraints'][i]['column'] + ') REFERENCES ' + $json['constraints'][i]['referenceTable'] + '(' + $json['constraints'][i]['referenceColumn'] + ')';
					}
				}

				// Code SQL complet
				var sqlCode = 'DROP TABLE IF EXISTS ' + $json['databaseName'] + '.' + $json['tableName'] + ' RESTRICT;\n\n';

				sqlCode = sqlCode + '/* Create table query */\n';
				sqlCode = sqlCode + 'CREATE TABLE IF NOT EXISTS ' + $json['databaseName'] + '.' + $json['tableName'] + '\n';
				sqlCode = sqlCode + '(id INTEGER AUTO_INCREMENT';

				sqlCode = sqlCode + createTable;

				sqlCode = sqlCode + ')\n';
				sqlCode = sqlCode + 'ENGINE = InnoDB;\n\n';

				sqlCode = sqlCode + '/* Select query */\n';
				sqlCode = sqlCode + selectQuery;
   				sqlCode = sqlCode + '/* Insert query */\n';
				sqlCode = sqlCode + insertColumns;
				sqlCode = sqlCode + insertValues;
   				sqlCode = sqlCode + '/* Update query */\n';
				sqlCode = sqlCode + updateQuery;
   				sqlCode = sqlCode + '/* Delete query */\n';
				sqlCode = sqlCode + deleteQuery;

				return sqlCode;
			};

			// On attache au bouton "Create code" l'événement qui génère le code de la classe Java.
			document.getElementById("create-code").onclick = function()
			{
				// On récupère la chaîne de caractères contenue dans la champ d'entrée.
				var jsonStr = document.getElementById("input").value; 
				
				// On supprime les sauts de ligne et les tabulations de la chaîne de caractères.
				jsonStr = jsonStr.replaceAll('\n', '').replaceAll('\t', ''); 

				// On convertit la chaîne de caractères en objet JSON interprétable.
				var jsonData = {};

				try
				{
					jsonData = JSON.parse(jsonStr);
				}
				catch ($error)
				{
					jsonData = {"error":jsonStr};
				}

				// On génère le code SQL.
				outputCode = createSQLcode(jsonData); 

				// On affiche le code généré dans le champ de sortie.
				document.getElementById("code-block").innerHTML = outputCode; 
			};

			// On attache au bouton "Copy code" l'événement qui copie le code généré dans le presse papier.
			document.getElementById("copy-code").onclick = function()
			{
				if (!navigator.clipboard)
				{
					var textArea = document.createElement("textarea");
					textArea.value = outputCode;
									
					textArea.style.top = "-1000000000px";
					textArea.style.left = "-1000000000px";
					textArea.style.width = "100px";
					textArea.style.height = "100px";
					textArea.style.position = "absolute";
									
					document.body.appendChild(textArea);
					textArea.focus();
					textArea.select();
									
					try
					{
						var successful = document.execCommand("copy");
										
						if (successful)
						{
							console.log('Copié !');
						} 
						else
						{
							console.log('La copie a échoué.');
						} 
					}
					catch ($error) 
					{
						console.log('La copie a échoué.');
						console.log($error);
					} 
									
					document.body.removeChild(textArea);
				}
				else
				{
					navigator.clipboard.writeText(outputCode).then(function()
					{
						console.log('Copié !');
					}, 
					function($error) 
					{
						console.log('La copie a échoué.');
						console.log($error);
					});
				}
			};
		</script>
	</body>
</html>

Et voici le code SQL généré par notre outil avec le JSON d'entrée que nous avons construit précédemment.

DROP TABLE IF EXISTS myDatabase.example RESTRICT;

/* Create table query */
CREATE TABLE IF NOT EXISTS myDatabase.example
(id INTEGER AUTO_INCREMENT,
id INTEGER DEFAULT 0 NOT NULL,
textColumn TEXT DEFAULT "" NOT NULL,
tinyintColumn TINYINT DEFAULT 0 NOT NULL,
id_truc INTEGER DEFAULT 0 NOT NULL,
floatColumn FLOAT DEFAULT 0.0 NOT NULL,
datetimeColumn DATETIME(6),
CONSTRAINT pk_example PRIMARY KEY (id),
CONSTRAINT fk_example_truc FOREIGN KEY (id_truc) REFERENCES truc(id))
ENGINE = InnoDB;

/* Select query */
SELECT example.id AS id,
	example.id AS id,
	example.textColumn AS textColumn,
	example.tinyintColumn AS tinyintColumn,
	example.id_truc AS id_truc,
	example.floatColumn AS floatColumn,
	example.datetimeColumn AS datetimeColumn
FROM myDatabase.example example;

/* Insert query */
INSERT INTO myDatabase.example(id,
	textColumn,
	tinyintColumn,
	id_truc,
	floatColumn,
	datetimeColumn)
VALUES (:id,
	:textColumn,
	:tinyintColumn,
	:id_truc,
	:floatColumn,
	:datetimeColumn);

/* Update query */
UPDATE myDatabase.example
SET id = :id,
	textColumn = :textColumn,
	tinyintColumn = :tinyintColumn,
	id_truc = :id_truc,
	floatColumn = :floatColumn,
	datetimeColumn = :datetimeColumn
WHERE id = :id;

/* Delete query */
DELETE FROM myDatabase.example WHERE id = :id;

Bon, maintenant, comme pour les classes Java, on va faire un petit outil qui permet de générer le code SQL pour une base de données complète.

Base de données complète

Le SQL faisant rarement l'objet d'une arborescence de fichiers de code source et étant généralement incorporé dans un autre langage, on ne va pas s'embêter à faire appel à Node.js ce coup-ci et on va rester dans une interface HTML, toujours du même type.
On va juste modifier un peu la fonction qui génère le code SQL.

Voici l'exemple de JSON d'entrée à mettre dans la balise textarea.
On déplace le paramètre du nom de la base de données à la racine du JSON et on ajoute un tableau qui contient la liste des tables.
Le JSON de chaque table est le même que précédemment.

{
	"databaseName": "myDatabase",
	"tables":
	[
		{
			"tableName": "example1",
			"columns":
			[
				{ "name": "id", "type": "INTEGER", "null": false, "default": "0" },
				{ "name": "textColumn", "type": "TEXT", "null": false, "default": "" },
				{ "name": "tinyintColumn", "type": "TINYINT", "null": false, "default": "0" },
				{ "name": "floatColumn", "type": "FLOAT", "null": false, "default": "0.0" },
				{ "name": "datetimeColumn", "type": "DATETIME(6)", "null": true, "default": null }
			],
			"constraints":
			[
				{ "name": "pk_example1", "type": "pk", "column": "id", "referenceColumn": "id" }
			]
		},
		{
			"tableName": "example2",
			"columns":
			[
				{ "name": "id", "type": "INTEGER", "null": false, "default": "0" },
				{ "name": "id_exemple1", "type": "INTEGER", "null": false, "default": "0" },
				{ "name": "textColumn", "type": "TEXT", "null": false, "default": "" },
				{ "name": "tinyintColumn", "type": "TINYINT", "null": false, "default": "0" },
				{ "name": "floatColumn", "type": "FLOAT", "null": false, "default": "0.0" },
				{ "name": "datetimeColumn", "type": "DATETIME(6)", "null": true, "default": null }
			],
			"constraints":
			[
				{ "name": "pk_example2", "type": "pk", "column": "id", "referenceColumn": "id" },
				{ "name": "fk_example2_example2", "type": "fk", "column": "id_exemple1", "referenceTable": "exemple1", "referenceColumn": "id" }
			]
		}
	]
}

Voici le code de la fonction. Comme d'habitude, les explications sont dans les commentaires.

var createSQLcode = function($json)
{
	// On regroupe les requêtes par type (DROP, CREATE, SELECT, INSERT, UPDATE, DELETE) et non pas par table

	var dropQuery = ''; // Code des requêtes de suppression des tables si elles existent
	var createTable = ''; // Code des requêtes de création de table
	var selectQuery = ''; // Code des requêtes de lecture
	var insertQuery = ''; // Code des requêtes d'insertion
	var updateQuery = ''; // Code des requêtes de mise à jour
	var deleteQuery = ''; // Code des requêtes de suppression de ligne

	// Boucle sur chaque table
	for (var i = 0; i < $json['tables'].length; i++)
	{
		var tableJSON = $json['tables'][i];

		var insertColumns = 'INSERT INTO ' + $json['databaseName'] + '.' + tableJSON['tableName'] + '('; // Code de la requête d'insertion (colonnes)
		var insertValues = 'VALUES ('; // Code de la requête d'insertion (valeurs)
					
		dropQuery = 'DROP TABLE IF EXISTS ' + $json['databaseName'] + '.' + tableJSON['tableName'] + ' RESTRICT;\n' + dropQuery;
		createTable = createTable + 'CREATE TABLE IF NOT EXISTS ' + $json['databaseName'] + '.' + tableJSON['tableName'] + '\n';
		createTable = createTable + '(id INTEGER AUTO_INCREMENT';
		selectQuery = selectQuery + "SELECT " + tableJSON['tableName'] + ".id AS id";
		updateQuery = updateQuery + 'UPDATE ' + $json['databaseName'] + '.' + tableJSON['tableName'] + '\nSET ';
		deleteQuery = deleteQuery + 'DELETE FROM ' + $json['databaseName'] + '.' + tableJSON['tableName'] + ' WHERE id = :id;\n';

		// Boucle sur chaque colonne
		for (var j = 0; j < tableJSON['columns'].length; j++)
		{
			// Création de table
			createTable = createTable + ',\n';
			createTable = createTable + tableJSON['columns'][j]['name'] + ' ' + tableJSON['columns'][j]['type'];

			if (tableJSON['columns'][j]['default'] !== null)
			{
				if (tableJSON['columns'][j]['type'] === 'TEXT' || tableJSON['columns'][j]['type'] === 'DATETIME')
				{
					createTable = createTable + ' DEFAULT "' + tableJSON['columns'][j]['default'] + '"';
				}
				else
				{
					createTable = createTable + ' DEFAULT ' + tableJSON['columns'][j]['default'];
				}
			}

			if (tableJSON['columns'][j]['null'] === false)
			{
				createTable = createTable + ' NOT NULL';
			}

			// Requête de sélection
			selectQuery = selectQuery + ',\n	';
			selectQuery = selectQuery + tableJSON['tableName'] + "." + tableJSON['columns'][j]['name'] + " AS " + tableJSON['columns'][j]['name'];

			// Requête d'insertion
			if (j > 0)
			{
				insertColumns = insertColumns + ',\n	';
				insertValues = insertValues + ',\n	';
			}

			insertColumns = insertColumns + tableJSON['columns'][j]['name'];
			insertValues = insertValues + ':' + tableJSON['columns'][j]['name'];

			// Requête de mise à jour
			if (j > 0)
			{
				updateQuery = updateQuery + ',\n	';
			}

			updateQuery = updateQuery + tableJSON['columns'][j]['name'] + ' = :' + tableJSON['columns'][j]['name'];
		}

		selectQuery = selectQuery + '\nFROM ' + $json['databaseName'] + '.' + tableJSON['tableName'] + ' ' + tableJSON['tableName'] + ';\n\n';
		insertColumns = insertColumns + ')\n';
		insertValues = insertValues + ');\n\n';
		insertQuery = insertQuery + insertColumns + insertValues;
		updateQuery = updateQuery + '\nWHERE id = :id;\n\n';

		// Boucle sur chaque contrainte
		for (var j = 0; j < tableJSON['constraints'].length; j++)
		{
			createTable = createTable + ',\n';
			createTable = createTable + 'CONSTRAINT ' + tableJSON['constraints'][j]['name'];

			if (tableJSON['constraints'][j]['type'] === 'pk')
			{
				createTable = createTable + ' PRIMARY KEY (' + tableJSON['constraints'][j]['referenceColumn'] + ')';
			}
			else
			{
				createTable = createTable + ' FOREIGN KEY (' + tableJSON['constraints'][j]['column'] + ') REFERENCES ' + tableJSON['constraints'][j]['referenceTable'] + '(' + tableJSON['constraints'][j]['referenceColumn'] + ')';
			}
		}

		createTable = createTable + ')\n';
		createTable = createTable + 'ENGINE = InnoDB;\n\n';
	}

	// Code SQL complet
	var sqlCode = '/* Drop tables query */\n';
	sqlCode = sqlCode + dropQuery;
	sqlCode = sqlCode + '\n/* Create tables query */\n';
	sqlCode = sqlCode + createTable;
	sqlCode = sqlCode + '/* Select query */\n';
	sqlCode = sqlCode + selectQuery;
	sqlCode = sqlCode + '/* Insert query */\n';
	sqlCode = sqlCode + insertQuery;
	sqlCode = sqlCode + '/* Update query */\n';
	sqlCode = sqlCode + updateQuery;
	sqlCode = sqlCode + '/* Delete query */\n';
	sqlCode = sqlCode + deleteQuery;

	return sqlCode;
};

Et enfin le code complet du nouvel outil que vous pouvez copier dans un fichier HTML.

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8" />
		<title>SQL database generator</title>

		<!-- Le CSS qui structure l'interface -->
		<style>

			body
			{
				position: absolute;
				left: 0px;
				right: 0px;
				top: 0px;
				bottom: 0px;
			}

			#input, #code-block
			{
				position: absolute;
				top: 10px;
				bottom: 40px;
			}

			#input
			{
				left: 10px;
				right: calc(50% + 7px);
				resize: none;
			}

			#code-block
			{
				left: calc(50% + 7px);
				right: 10px;
				margin: 0px;
				padding: 10px;
				border: 1px solid black;
				overflow: auto;
			}

			#create-code-block, #copy-code-block
			{
				position: absolute;
				bottom: 0px;
				height: 30px;
				margin: 0px;
				text-align: center;
			}

			#create-code-block
			{
				left: 10px;
				right: calc(50% + 7px);
			}

			#copy-code-block
			{
				left: calc(50% + 7px);
				right: 10px;
			}

		</style>
	</head>

	<body>
		<!-- Champ de saisie du JSON d'entrée -->
		<textarea id="input" >
{
	"databaseName": "myDatabase",
	"tables":
	[
		{
			"tableName": "example1",
			"columns":
			[
				{ "name": "id", "type": "INTEGER", "null": false, "default": "0" },
				{ "name": "textColumn", "type": "TEXT", "null": false, "default": "" },
				{ "name": "tinyintColumn", "type": "TINYINT", "null": false, "default": "0" },
				{ "name": "floatColumn", "type": "FLOAT", "null": false, "default": "0.0" },
				{ "name": "datetimeColumn", "type": "DATETIME(6)", "null": true, "default": null }
			],
			"constraints":
			[
				{ "name": "pk_example1", "type": "pk", "column": "id", "referenceColumn": "id" }
			]
		},
		{
			"tableName": "example2",
			"columns":
			[
				{ "name": "id", "type": "INTEGER", "null": false, "default": "0" },
				{ "name": "id_exemple1", "type": "INTEGER", "null": false, "default": "0" },
				{ "name": "textColumn", "type": "TEXT", "null": false, "default": "" },
				{ "name": "tinyintColumn", "type": "TINYINT", "null": false, "default": "0" },
				{ "name": "floatColumn", "type": "FLOAT", "null": false, "default": "0.0" },
				{ "name": "datetimeColumn", "type": "DATETIME(6)", "null": true, "default": null }
			],
			"constraints":
			[
				{ "name": "pk_example2", "type": "pk", "column": "id", "referenceColumn": "id" },
				{ "name": "fk_example2_example2", "type": "fk", "column": "id_exemple1", "referenceTable": "exemple1", "referenceColumn": "id" }
			]
		}
	]
}
		</textarea>
		
		<!-- Bloc qui affiche le code généré -->
		<pre id="code-block" ></pre>
		
		<!-- Bouton qui déclenche la génération du code -->
		<p id="create-code-block" >
			<input id="create-code" type="button" value="Create code" />
		</p>

		<!-- Bouton qui déclenche la copy du code généré -->
		<p id="copy-code-block" >
			<input id="copy-code" type="button" value="Copy code" />
		</p>

		<script type="text/javascript" >
			var outputCode = ''; // Variable qui stocke le code généré.

			var createSQLcode = function($json)
			{
				// On regroupe les requêtes par type (DROP, CREATE, SELECT, INSERT, UPDATE, DELETE) et non pas par table

				var dropQuery = ''; // Code des requêtes de suppression des tables si elles existent
				var createTable = ''; // Code des requêtes de création de table
				var selectQuery = ''; // Code des requêtes de lecture
				var insertQuery = ''; // Code des requêtes d'insertion
				var updateQuery = ''; // Code des requêtes de mise à jour
				var deleteQuery = ''; // Code des requêtes de suppression de ligne

				// Boucle sur chaque table
				for (var i = 0; i < $json['tables'].length; i++)
				{
					var tableJSON = $json['tables'][i];

					var insertColumns = 'INSERT INTO ' + $json['databaseName'] + '.' + tableJSON['tableName'] + '('; // Code de la requête d'insertion (colonnes)
					var insertValues = 'VALUES ('; // Code de la requête d'insertion (valeurs)
					
					dropQuery = 'DROP TABLE IF EXISTS ' + $json['databaseName'] + '.' + tableJSON['tableName'] + ' RESTRICT;\n' + dropQuery;
					createTable = createTable + 'CREATE TABLE IF NOT EXISTS ' + $json['databaseName'] + '.' + tableJSON['tableName'] + '\n';
					createTable = createTable + '(id INTEGER AUTO_INCREMENT';
					selectQuery = selectQuery + "SELECT " + tableJSON['tableName'] + ".id AS id";
					updateQuery = updateQuery + 'UPDATE ' + $json['databaseName'] + '.' + tableJSON['tableName'] + '\nSET ';
					deleteQuery = deleteQuery + 'DELETE FROM ' + $json['databaseName'] + '.' + tableJSON['tableName'] + ' WHERE id = :id;\n';

					// Boucle sur chaque colonne
					for (var j = 0; j < tableJSON['columns'].length; j++)
					{
						// Création de table
						createTable = createTable + ',\n';
						createTable = createTable + tableJSON['columns'][j]['name'] + ' ' + tableJSON['columns'][j]['type'];

						if (tableJSON['columns'][j]['default'] !== null)
						{
							if (tableJSON['columns'][j]['type'] === 'TEXT' || tableJSON['columns'][j]['type'] === 'DATETIME')
							{
								createTable = createTable + ' DEFAULT "' + tableJSON['columns'][j]['default'] + '"';
							}
							else
							{
								createTable = createTable + ' DEFAULT ' + tableJSON['columns'][j]['default'];
							}
						}

						if (tableJSON['columns'][j]['null'] === false)
						{
							createTable = createTable + ' NOT NULL';
						}

						// Requête de sélection
						selectQuery = selectQuery + ',\n	';
						selectQuery = selectQuery + tableJSON['tableName'] + "." + tableJSON['columns'][j]['name'] + " AS " + tableJSON['columns'][j]['name'];

						// Requête d'insertion
						if (j > 0)
						{
							insertColumns = insertColumns + ',\n	';
							insertValues = insertValues + ',\n	';
						}

						insertColumns = insertColumns + tableJSON['columns'][j]['name'];
						insertValues = insertValues + ':' + tableJSON['columns'][j]['name'];

						// Requête de mise à jour
						if (j > 0)
						{
							updateQuery = updateQuery + ',\n	';
						}

						updateQuery = updateQuery + tableJSON['columns'][j]['name'] + ' = :' + tableJSON['columns'][j]['name'];
					}

					selectQuery = selectQuery + '\nFROM ' + $json['databaseName'] + '.' + tableJSON['tableName'] + ' ' + tableJSON['tableName'] + ';\n\n';
					insertColumns = insertColumns + ')\n';
					insertValues = insertValues + ');\n\n';
					insertQuery = insertQuery + insertColumns + insertValues;
					updateQuery = updateQuery + '\nWHERE id = :id;\n\n';

					// Boucle sur chaque contrainte
					for (var j = 0; j < tableJSON['constraints'].length; j++)
					{
						createTable = createTable + ',\n';
						createTable = createTable + 'CONSTRAINT ' + tableJSON['constraints'][j]['name'];

						if (tableJSON['constraints'][j]['type'] === 'pk')
						{
							createTable = createTable + ' PRIMARY KEY (' + tableJSON['constraints'][j]['referenceColumn'] + ')';
						}
						else
						{
							createTable = createTable + ' FOREIGN KEY (' + tableJSON['constraints'][j]['column'] + ') REFERENCES ' + tableJSON['constraints'][j]['referenceTable'] + '(' + tableJSON['constraints'][j]['referenceColumn'] + ')';
						}
					}

					createTable = createTable + ')\n';
					createTable = createTable + 'ENGINE = InnoDB;\n\n';
				}

				// Code SQL complet
				var sqlCode = '/* Drop tables query */\n';
				sqlCode = sqlCode + dropQuery;
				sqlCode = sqlCode + '\n/* Create tables query */\n';
				sqlCode = sqlCode + createTable;
				sqlCode = sqlCode + '/* Select query */\n';
				sqlCode = sqlCode + selectQuery;
				sqlCode = sqlCode + '/* Insert query */\n';
				sqlCode = sqlCode + insertQuery;
				sqlCode = sqlCode + '/* Update query */\n';
				sqlCode = sqlCode + updateQuery;
				sqlCode = sqlCode + '/* Delete query */\n';
				sqlCode = sqlCode + deleteQuery;

				return sqlCode;
			};

			// On attache au bouton "Create code" l'événement qui génère le code de la classe Java.
			document.getElementById("create-code").onclick = function()
			{
				// On récupère la chaîne de caractères contenue dans la champ d'entrée.
				var jsonStr = document.getElementById("input").value; 
				
				// On supprime les sauts de ligne et les tabulations de la chaîne de caractères.
				jsonStr = jsonStr.replaceAll('\n', '').replaceAll('\t', ''); 

				// On convertit la chaîne de caractères en objet JSON interprétable.
				var jsonData = {};

				try
				{
					jsonData = JSON.parse(jsonStr);
				}
				catch ($error)
				{
					jsonData = {"error":jsonStr};
				}

				// On génère le code SQL.
				outputCode = createSQLcode(jsonData); 

				// On affiche le code généré dans le champ de sortie.
				document.getElementById("code-block").innerHTML = outputCode; 
			};

			// On attache au bouton "Copy code" l'événement qui copie le code généré dans le presse papier.
			document.getElementById("copy-code").onclick = function()
			{
				if (!navigator.clipboard)
				{
					var textArea = document.createElement("textarea");
					textArea.value = outputCode;
									
					textArea.style.top = "-1000000000px";
					textArea.style.left = "-1000000000px";
					textArea.style.width = "100px";
					textArea.style.height = "100px";
					textArea.style.position = "absolute";
									
					document.body.appendChild(textArea);
					textArea.focus();
					textArea.select();
									
					try
					{
						var successful = document.execCommand("copy");
										
						if (successful)
						{
							console.log('Copié !');
						} 
						else
						{
							console.log('La copie a échoué.');
						} 
					}
					catch ($error) 
					{
						console.log('La copie a échoué.');
						console.log($error);
					} 
									
					document.body.removeChild(textArea);
				}
				else
				{
					navigator.clipboard.writeText(outputCode).then(function()
					{
						console.log('Copié !');
					}, 
					function($error) 
					{
						console.log('La copie a échoué.');
						console.log($error);
					});
				}
			};
		</script>
	</body>
</html>

Et voici le code SQL que vous obtenez avec le JSON d'entrée d'exemple.

/* Drop tables query */
DROP TABLE IF EXISTS myDatabase.example2 RESTRICT;
DROP TABLE IF EXISTS myDatabase.example1 RESTRICT;

/* Create tables query */
CREATE TABLE IF NOT EXISTS myDatabase.example1
(id INTEGER AUTO_INCREMENT,
id INTEGER DEFAULT 0 NOT NULL,
textColumn TEXT DEFAULT "" NOT NULL,
tinyintColumn TINYINT DEFAULT 0 NOT NULL,
floatColumn FLOAT DEFAULT 0.0 NOT NULL,
datetimeColumn DATETIME(6),
CONSTRAINT pk_example1 PRIMARY KEY (id))
ENGINE = InnoDB;

CREATE TABLE IF NOT EXISTS myDatabase.example2
(id INTEGER AUTO_INCREMENT,
id INTEGER DEFAULT 0 NOT NULL,
id_exemple1 INTEGER DEFAULT 0 NOT NULL,
textColumn TEXT DEFAULT "" NOT NULL,
tinyintColumn TINYINT DEFAULT 0 NOT NULL,
floatColumn FLOAT DEFAULT 0.0 NOT NULL,
datetimeColumn DATETIME(6),
CONSTRAINT pk_example2 PRIMARY KEY (id),
CONSTRAINT fk_example2_example2 FOREIGN KEY (id_exemple1) REFERENCES exemple1(id))
ENGINE = InnoDB;

/* Select query */
SELECT example1.id AS id,
	example1.id AS id,
	example1.textColumn AS textColumn,
	example1.tinyintColumn AS tinyintColumn,
	example1.floatColumn AS floatColumn,
	example1.datetimeColumn AS datetimeColumn
FROM myDatabase.example1 example1;

SELECT example2.id AS id,
	example2.id AS id,
	example2.id_exemple1 AS id_exemple1,
	example2.textColumn AS textColumn,
	example2.tinyintColumn AS tinyintColumn,
	example2.floatColumn AS floatColumn,
	example2.datetimeColumn AS datetimeColumn
FROM myDatabase.example2 example2;

/* Insert query */
INSERT INTO myDatabase.example1(id,
	textColumn,
	tinyintColumn,
	floatColumn,
	datetimeColumn)
VALUES (:id,
	:textColumn,
	:tinyintColumn,
	:floatColumn,
	:datetimeColumn);

INSERT INTO myDatabase.example2(id,
	id_exemple1,
	textColumn,
	tinyintColumn,
	floatColumn,
	datetimeColumn)
VALUES (:id,
	:id_exemple1,
	:textColumn,
	:tinyintColumn,
	:floatColumn,
	:datetimeColumn);

/* Update query */
UPDATE myDatabase.example1
SET id = :id,
	textColumn = :textColumn,
	tinyintColumn = :tinyintColumn,
	floatColumn = :floatColumn,
	datetimeColumn = :datetimeColumn
WHERE id = :id;

UPDATE myDatabase.example2
SET id = :id,
	id_exemple1 = :id_exemple1,
	textColumn = :textColumn,
	tinyintColumn = :tinyintColumn,
	floatColumn = :floatColumn,
	datetimeColumn = :datetimeColumn
WHERE id = :id;

/* Delete query */
DELETE FROM myDatabase.example1 WHERE id = :id;
DELETE FROM myDatabase.example2 WHERE id = :id;

Notez que je n'ai pas automatisé la génération de requêtes avec des jointures.
En fait, les possibilités étant infinies je ne peux pas toutes les couvrir. Ceci étant dit, si vous repérez un schéma de requête récurrent dans votre projet, n'hésitez surtout pas à modifier l'outil pour l'y inclure. C'est précisément ce qu'il faut faire.

Construire une bibliothèque de blocs de code

Bon, c'est bien joli tout ça, mais, dans le cas de la programmation orientée objet, il reste quand même à écrire le code des méthodes à la mano.
Pas de panique, là aussi vous pouvez accélérer le processus manuellement avant de dégainer un quelconque chat bot !

Le principe est tout simple.
Il s'agit juste de compiler dans un fichier tous les blocs de codes que vous êtes sûrs de réutiliser dans plusieurs projets mais que vous n'utilisez pas assez souvent pour vous en souvenir par coeur.
Vous pouvez aussi y mettre aussi des bouts de code que vous connaissez par coeur mais qui sont un peu longs et lourds à réécrire.

L'idée est de regrouper toute votre base de connaissance dans un coin où elle sera facile d'accès pour ne pas avoir à fouiller à chaque fois dans vos vieux codes source pour retrouver un bout de code et pour reporter ce bout de code par un simple copier coller.

Vous pouvez faire ça dans un tableur classique, mais moi je préfère le faire dans un fichier HTML, comme ça je peux maîtriser davantage la mise en page et ajouter quelques fonctionnalités avec un bout de javascript pour plus de confort.
Je vous montre ça !

D'abord, voici la structure de base du HTML avec le petit bout de Javascript qui va avec.
Comme d'habitude, le code est expliqué dans les commentaires.

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8" />
		<title>My code library</title>

		<!-- Le CSS qui structure l'interface -->
		<style>
			body
			{
				font-family: Arial, Helvetica, sans-serif;
			}

			table
			{
				border-collapse: collapse;
				width: 100%;
			}

			table th,
			table td
			{
				padding: 10px;
				border: 1px solid rgb(0, 0, 0);
			}

			pre
			{
				padding: 0px;
				margin: 0px;
				max-height: 500px;
				overflow: auto;
			}
		</style>
        
   		<!-- Le petit bout de javascript qui permet de copier un bloc de code en un clic. -->
        <script type="text/javascript" >
			var toClipboard = function($codeBlockId)
			{
				var codeBlock = document.getElementById($codeBlockId);

				if (!navigator.clipboard)
				{
					var textArea = document.createElement("textarea");
					textArea.value = codeBlock.innerText;
						
					textArea.style.top = "-1000000000px";
					textArea.style.left = "-1000000000px";
					textArea.style.width = "100px";
					textArea.style.height = "100px";
					textArea.style.position = "absolute";
						
					document.body.appendChild(textArea);
					textArea.focus();
					textArea.select();
						
					try
					{
						var successful = document.execCommand("copy");
										
						if (successful)
						{
							console.log('Copié !');
						} 
						else
						{
							console.log('La copie a échoué.');
						} 
					}
					catch ($error) 
					{
						console.log('La copie a échoué.');
						console.log($error);
					} 
						
					document.body.removeChild(textArea);
				}
				else
				{
					navigator.clipboard.writeText(codeBlock.innerText).then(function()
					{
						console.log('Copié !');
					}, 
					function($error) 
					{
						console.log('La copie a échoué.');
						console.log($error);
					});
				}

				setTimeout(function() { copyConsole.innerHTML = ''; }, 5000);
			};
		</script>
	</head>

	<body>
		<h1>Javascript library</h1>
		<table>
			<!-- En-tête des colonnes -->
			<thead>
				<th>Name</th><th>Copy</th><th>Code</th><th>Keywords</th>
			</thead>
			<!-- Liste de tous les blocs de code -->
			<tbody>
				<!-- Un exemple de bloc de code réutilisable -->
				<tr>
					<td>Nom du bloc de code</td>
					<td>
						<!-- Le bouton qui permet de copier en un clic ! Il appelle un bout de Javascript. -->
						<input type="button" value="Copy" onclick="toClipboard('block-1');" />
					</td>
					<td>
						<!--  Le bloc de code. Il lui faut un id pour pouvoir indiquer le contenu de quel bloc le bouton doit copier. -->
						<pre id="block-1" >
// Placer ici le bloc de code à copier
						</pre>
					</td>
					<td>mot clé 1, mot clé 2</td>
				</tr>
			</tbody>
		</table>
	</body>
</html>

Maintenant que vous avez la structure du fichier HTML, je vous propose d'insérer un bloc de code d'exemple.

Vos noterez qu'il y a justement un bloc de code Javascript que je vous fais mettre dans tous les exemples depuis tout à l'heure : celui qui copie un contenu dans le presse papier.
Il est d'ailleurs aussi présent sur ce blog pour vous permettre de copier mes exemples de code en un clic.
Ça en fait un parfait candidat pour figurer dans notre petite librairie. 😁

Voici mon exemple.

<!-- Un exemple de bloc de code réutilisable -->
<tr>
	<td>To clipboard</td>
	<td>
		<!-- Le bouton qui permet de copier en un clic ! Il appelle un bout de Javascript. -->
		<input type="button" value="Copy" onclick="toClipboard('block-1');" />
	</td>
	<td>
		<!--  Le bloc de code. Il lui faut un id pour pouvoir indiquer le contenu de quel bloc le bouton doit copier. -->
		<pre id="block-1" >
if (!navigator.clipboard)
{
	var textArea = document.createElement("textarea");
	textArea.value = "Hello world !";
								
	textArea.style.top = "-1000000000px";
	textArea.style.left = "-1000000000px";
	textArea.style.width = "100px";
	textArea.style.height = "100px";
	textArea.style.position = "absolute";
								
	document.body.appendChild(textArea);
	textArea.focus();
	textArea.select();
								
	try
	{
		var successful = document.execCommand("copy");
									
		if (successful)
		{
			// Something to do...
		} 
		else
		{
			// Something to do...
		} 
	}
	catch ($error) 
	{
		// Something to do...
	} 
								
	document.body.removeChild(textArea);
}
else
{
	navigator.clipboard.writeText("Hello world !").then(function()
	{
		// Something to do...
	}, 
	function($error) 
	{
		// Something to do...
	});
}
		</pre>
	</td>
	<td>clipboard, copy, paste</td>
</tr>

Et pour finir, voici le code complet comprenant cet exemple.

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8" />
		<title>My code library</title>

		<!-- Le CSS qui structure l'interface -->
		<style>
			body
			{
				font-family: Arial, Helvetica, sans-serif;
			}

			table
			{
				border-collapse: collapse;
				width: 100%;
			}

			table th,
			table td
			{
				padding: 10px;
				border: 1px solid rgb(0, 0, 0);
			}

			pre
			{
				padding: 0px;
				margin: 0px;
				max-height: 500px;
				overflow: auto;
			}
		</style>
        
   		<!-- Le petit bout de javascript qui permet de copier un bloc de code en un clic. -->
        <script type="text/javascript" >
			var toClipboard = function($codeBlockId)
			{
				var codeBlock = document.getElementById($codeBlockId);

				if (!navigator.clipboard)
				{
					var textArea = document.createElement("textarea");
					textArea.value = codeBlock.innerText;
						
					textArea.style.top = "-1000000000px";
					textArea.style.left = "-1000000000px";
					textArea.style.width = "100px";
					textArea.style.height = "100px";
					textArea.style.position = "absolute";
						
					document.body.appendChild(textArea);
					textArea.focus();
					textArea.select();
						
					try
					{
						var successful = document.execCommand("copy");
										
						if (successful)
						{
							console.log('Copié !');
						} 
						else
						{
							console.log('La copie a échoué.');
						} 
					}
					catch ($error) 
					{
						console.log('La copie a échoué.');
						console.log($error);
					} 
						
					document.body.removeChild(textArea);
				}
				else
				{
					navigator.clipboard.writeText(codeBlock.innerText).then(function()
					{
						console.log('Copié !');
					}, 
					function($error) 
					{
						console.log('La copie a échoué.');
						console.log($error);
					});
				}

				setTimeout(function() { copyConsole.innerHTML = ''; }, 5000);
			};
		</script>
	</head>

	<body>
		<h1>Javascript library</h1>
		<table>
			<!-- En-tête des colonnes -->
			<thead>
				<th>Name</th><th>Copy</th><th>Code</th><th>Keywords</th>
			</thead>
			<!-- Liste de tous les blocs de code -->
			<tbody>
				<!-- Un exemple de bloc de code réutilisable -->
				<tr>
					<td>To clipboard</td>
					<td>
						<!-- Le bouton qui permet de copier en un clic ! Il appelle un bout de Javascript. -->
						<input type="button" value="Copy" onclick="toClipboard('block-1');" />
					</td>
					<td>
						<!--  Le bloc de code. Il lui faut un id pour pouvoir indiquer le contenu de quel bloc le bouton doit copier. -->
						<pre id="block-1" >
if (!navigator.clipboard)
{
	var textArea = document.createElement("textarea");
	textArea.value = "Hello world !";
								
	textArea.style.top = "-1000000000px";
	textArea.style.left = "-1000000000px";
	textArea.style.width = "100px";
	textArea.style.height = "100px";
	textArea.style.position = "absolute";
								
	document.body.appendChild(textArea);
	textArea.focus();
	textArea.select();
								
	try
	{
		var successful = document.execCommand("copy");
									
		if (successful)
		{
			// Something to do...
		} 
		else
		{
			// Something to do...
		} 
	}
	catch ($error) 
	{
		// Something to do...
	} 
								
	document.body.removeChild(textArea);
}
else
{
	navigator.clipboard.writeText("Hello world !").then(function()
	{
		// Something to do...
	}, 
	function($error) 
	{
		// Something to do...
	});
}
						</pre>
					</td>
					<td>clipboard, copy, paste</td>
				</tr>
			</tbody>
		</table>
	</body>
</html>

Voilà ! Avec tout ça, il ne vous restera à plus qu'à réfléchir/faire des recherches et à taper du code à la main que pour ce que vous n'avez jamais fait !
C'est justement là que vous progresserez et apprendrez de nouvelles choses.

Remettre l'IA à sa place

Bon, je tape sur l'IA depuis tout à l'heure, mais en vérité je ne vous demande pas de la bannir de votre processus de développement.
Au contraire, elle peut être utile pour certaines tâches vraiment chiantes et intellectuellement pas stimulantes, mais qui ne peuvent pas être automatisées avec un simple script de concaténation de texte.

Pour vous montrer quelques cas d'usage, je vais utiliser Le Chat de Mistral AI, le concurrent français de Chat GPT.

Page d'accueil du chat de Mistral

⚠️

Notez que vous pouvez utiliser Le Chat sans compte et gratuitement. Sachez seulement qu'à l'heure où j'écris ces lignes, sans abonnement vous êtes limité à 20 requêtes par jour, donc, lorsque vous prévoyez une session de chat, préparez soigneusement vos prompts. De toute façon, compte tenu de l'énergie que les IA consomment, c'est pas plus mal de les utiliser avec parcimonie.

Générer des données factices pour faire des tests

Viendra un moment où vous aurez besoin de tester votre projet en y insérant des données bidons.
Seulement, les données bidons, c'est très répétitif à écrire tout en vous pressant le citron pour trouver des idées. Du temps et de l'énergie dépensées pour une tâche proposant peu de valeur ajoutée.
C'est le genre de tâche pour lequel vous pouvez solliciter les chat bots.

Imaginons que vous avez développé une application de carnet de contacts et que vous souhaitez y insérer des contacts factices pour la tester.
Voici un exemple de prompt pour demander au Chat de Mistral de générer un paquet de données.

Peux-tu générer une liste de 20 contacts en respectant la structure json suivante ? 

{
    "firstname": "Pierre",
    "name": "Dutronc",
    "phone": "0236495786",
    "mail": "pierre.dutronc@gmail.com"
}

Voici la réponse de Mistral qui propose bien une vingtaine d'exemples de faux contacts formatés comme demandé en plus de l'exemple que je lui ai fourni.

[
    {
        "firstname": "Pierre",
        "name": "Dutronc",
        "phone": "0236495786",
        "mail": "pierre.dutronc@gmail.com"
    },
    {
        "firstname": "Marie",
        "name": "Martin",
        "phone": "0612345678",
        "mail": "marie.martin@yahoo.fr"
    },
    {
        "firstname": "Jean",
        "name": "Dupont",
        "phone": "0798765432",
        "mail": "jean.dupont@hotmail.com"
    },
    {
        "firstname": "Sophie",
        "name": "Lefevre",
        "phone": "0645678901",
        "mail": "sophie.lefevre@gmail.com"
    },
    {
        "firstname": "Paul",
        "name": "Durand",
        "phone": "0734567890",
        "mail": "paul.durand@outlook.fr"
    },
    {
        "firstname": "Claire",
        "name": "Moreau",
        "phone": "0623456789",
        "mail": "claire.moreau@gmail.com"
    },
    {
        "firstname": "Luc",
        "name": "Fournier",
        "phone": "0712345678",
        "mail": "luc.fournier@yahoo.fr"
    },
    {
        "firstname": "Emma",
        "name": "Girard",
        "phone": "0698765432",
        "mail": "emma.girard@hotmail.com"
    },
    {
        "firstname": "Marc",
        "name": "Rousseau",
        "phone": "0789012345",
        "mail": "marc.rousseau@gmail.com"
    },
    {
        "firstname": "Julie",
        "name": "Lemaire",
        "phone": "0678901234",
        "mail": "julie.lemaire@outlook.fr"
    },
    {
        "firstname": "Antoine",
        "name": "Chevalier",
        "phone": "0767890123",
        "mail": "antoine.chevalier@gmail.com"
    },
    {
        "firstname": "Laura",
        "name": "Bernard",
        "phone": "0656789012",
        "mail": "laura.bernard@yahoo.fr"
    },
    {
        "firstname": "Thomas",
        "name": "Morin",
        "phone": "0745678901",
        "mail": "thomas.morin@hotmail.com"
    },
    {
        "firstname": "Anaïs",
        "name": "Simon",
        "phone": "0634567890",
        "mail": "anais.simon@gmail.com"
    },
    {
        "firstname": "David",
        "name": "Michel",
        "phone": "0723456789",
        "mail": "david.michel@outlook.fr"
    },
    {
        "firstname": "Chloé",
        "name": "Laurent",
        "phone": "0612345678",
        "mail": "chloe.laurent@gmail.com"
    },
    {
        "firstname": "Nicolas",
        "name": "Dumont",
        "phone": "0798765432",
        "mail": "nicolas.dumont@yahoo.fr"
    },
    {
        "firstname": "Camille",
        "name": "Blanc",
        "phone": "0689012345",
        "mail": "camille.blanc@hotmail.com"
    },
    {
        "firstname": "Vincent",
        "name": "Roux",
        "phone": "0778901234",
        "mail": "vincent.roux@gmail.com"
    },
    {
        "firstname": "Élodie",
        "name": "Garnier",
        "phone": "0667890123",
        "mail": "elodie.garnier@outlook.fr"
    },
    {
        "firstname": "Alexandre",
        "name": "Joly",
        "phone": "0756789012",
        "mail": "alexandre.joly@gmail.com"
    }
]

Notez que si vous trouvez dans cette liste les noms de personnes qui existent vraiment, c'est tout à fait fortuit.

Générer des commentaires et de la documentation

Un jour ou l'autre, il vous arrivera un autre truc très très très TRÈS chiant : devoir lire, comprendre et documenter un code que vous n'avez pas écrit et dont l'auteur est parti sans laisser de documentation ni de commentaire (c'est courant comme situation).
Si par dessus le marché le code est écrit salement, vous pouvez ajouter 10 points supplémentaires de chiantitude. 😓

Dans cette situation, les chat bots peuvent aussi vous soulager.

On va faire une essai ensemble.
J'ai repris un de mes bouts de codes dont j'ai enlevé tous les commentaires et que j'ai essayé de rendre aussi désagréable à lire que possible. Notez cependant qu'il est fonctionnel.

var dr = []; var acc = []; var re = [];

var onReady = function(callback){
	if(re.length >= acc.length)
		callback(re);
};

var loadFile = function(e, callback){
	e.preventDefault();
    
	dr = []; acc = []; re = [];
	var ea = e.dataTransfer.effectAllowed;

	if (ea === "all"){
		dr = e.dataTransfer.files;
					
		for(var i=0; i < dr.length; i++){
			if(accept.length <= 0 || accept.indexOf(dr[i].type) >= 0)
				acc.push(dr[i]);
		}

		for(var i = 0; i < acc.length; i++){
			var f = acc[i]; var r = new FileReader(); r.n = f.name; r.t = f.type; r.p = f.path;
						
			r.onload = function (e){
				var fd = e.target.result; var fn = this.n; var ft = this.t; var fp = this.p;
				re.push({ n: fn, p: fp, t: ft, d: fd });
					onReady(callback);
			};
						
			if(/^image/.test(f.type) || /^application\/pdf/.test(f.type))
				r.readAsDataURL(f);
			else if(/^text/.test(f.type) || f.type === 'application/json')
				r.readAsText(f);
		}
	}
};

Et voici le prompt que nous allons envoyer au chat de Mistral.

J'ai besoin de documenter le code source d'un projet écrit en javascript qui n'a pas été commenté. 
Peux tu expliquer ce que fait cet extrait de code et y insérer des commentaires ? 

var dr = []; var acc = []; var re = [];

var onReady = function(callback){
	if(re.length >= acc.length)
		callback(re);
};

var loadFile = function(e, callback){
	e.preventDefault();
    
	dr = []; acc = []; re = [];
	var ea = e.dataTransfer.effectAllowed;

	if (ea === "all"){
		dr = e.dataTransfer.files;
					
		for(var i=0; i < dr.length; i++){
			if(accept.length <= 0 || accept.indexOf(dr[i].type) >= 0)
				acc.push(dr[i]);
		}

		for(var i = 0; i < acc.length; i++){
			var f = acc[i]; var r = new FileReader(); r.n = f.name; r.t = f.type; r.p = f.path;
						
			r.onload = function (e){
				var fd = e.target.result; var fn = this.n; var ft = this.t; var fp = this.p;
				re.push({ n: fn, p: fp, t: ft, d: fd });
					onReady(callback);
			};
						
			if(/^image/.test(f.type) || /^application\/pdf/.test(f.type))
				r.readAsDataURL(f);
			else if(/^text/.test(f.type) || f.type === 'application/json')
				r.readAsText(f);
		}
	}
};

Le chat fournit une réponse en 2 parties : la première contient le code que je lui ai fourni, mais enrichi de commentaires, la seconde explique ce que fait mon extrait de code.

Voici le code commenté par le chat. Notez qu'il a aussi pris la peine de mettre des sauts de ligne entre des instructions que j'avais mises sur une seule ligne.

// Initialisation des tableaux pour stocker les fichiers drag-and-drop, les fichiers acceptés et les résultats de lecture
var dr = []; var acc = []; var re = [];

// Fonction appelée lorsque les fichiers sont prêts à être traités
var onReady = function(callback){
    // Vérifie si tous les fichiers acceptés ont été lus
    if(re.length >= acc.length)
        // Appelle la fonction de rappel avec les résultats de lecture
        callback(re);
};

// Fonction pour charger les fichiers à partir d'un événement de drag-and-drop
var loadFile = function(e, callback){
    // Empêche le comportement par défaut de l'événement
    e.preventDefault();

    // Réinitialise les tableaux
    dr = []; acc = []; re = [];

    // Récupère les effets autorisés pour le drag-and-drop
    var ea = e.dataTransfer.effectAllowed;

    // Vérifie si tous les types de fichiers sont autorisés
    if (ea === "all"){
        // Récupère les fichiers de l'événement de drag-and-drop
        dr = e.dataTransfer.files;

        // Filtre les fichiers acceptés en fonction de leur type
        for(var i=0; i < dr.length; i++){
            if(accept.length <= 0 || accept.indexOf(dr[i].type) >= 0)
                acc.push(dr[i]);
        }

        // Lit chaque fichier accepté
        for(var i = 0; i < acc.length; i++){
            var f = acc[i];
            var r = new FileReader();
            r.n = f.name;
            r.t = f.type;
            r.p = f.path;

            // Fonction appelée lorsque la lecture du fichier est terminée
            r.onload = function (e){
                var fd = e.target.result;
                var fn = this.n;
                var ft = this.t;
                var fp = this.p;
                // Ajoute les informations du fichier lu au tableau des résultats
                re.push({ n: fn, p: fp, t: ft, d: fd });
                // Appelle la fonction onReady pour vérifier si tous les fichiers sont prêts
                onReady(callback);
            };

            // Lit le fichier en fonction de son type
            if(/^image/.test(f.type) || /^application\/pdf/.test(f.type))
                r.readAsDataURL(f);
            else if(/^text/.test(f.type) || f.type === 'application/json')
                r.readAsText(f);
        }
    }
};

Et voici l'explication du code fournie par le chat.

  1. Initialisation des tableaux :

    • dr : Tableau pour stocker les fichiers obtenus à partir de l'événement de drag-and-drop.
    • acc : Tableau pour stocker les fichiers acceptés en fonction de leur type.
    • re : Tableau pour stocker les résultats de la lecture des fichiers.
  2. Fonction onReady :

    • Vérifie si tous les fichiers acceptés ont été lus.
    • Si c'est le cas, appelle la fonction de rappel (callback) avec les résultats de lecture.
  3. Fonction loadFile :

    • Empêche le comportement par défaut de l'événement de drag-and-drop.
    • Réinitialise les tableaux dr, acc et re.
    • Récupère les effets autorisés pour le drag-and-drop.
    • Si tous les types de fichiers sont autorisés, récupère les fichiers de l'événement de drag-and-drop.
    • Filtre les fichiers acceptés en fonction de leur type.
    • Lit chaque fichier accepté en utilisant un FileReader.
    • Ajoute les informations du fichier lu au tableau des résultats.
    • Appelle la fonction onReady pour vérifier si tous les fichiers sont prêts.
    • Lit le fichier en fonction de son type (image, PDF, texte, JSON).

Certains commentaires sont un peu littéraux et n'expliquent pas ce que signifie l'instruction fondamentalement, mais le résultat est majoritairement satisfaisant.

On va pousser plus loin en lui demandant de réécrire le code en employant des noms de variables plus clairs pour un lecteur humain.
Pour cela, dans la même session de chat, j'ai simplement envoyé le prompt suivant.

  Peux-tu réécrire ce même extrait de code en concervant les commentaires que tu as ajoutés et en changeant les noms de variables de façon à ce qu'ils soient compréhensibles par un lecteur humain ? 

Encore une fois, le chat me propose une réponse en 2 parties : code et explications.

Voici le code modifié et commenté qu'il me propose. Je l'ai testé, il fonctionne.

// Initialisation des tableaux pour stocker les fichiers drag-and-drop, les fichiers acceptés et les résultats de lecture
var droppedFiles = [];
var acceptedFiles = [];
var readResults = [];

// Fonction appelée lorsque les fichiers sont prêts à être traités
var onFilesReady = function(callback){
    // Vérifie si tous les fichiers acceptés ont été lus
    if(readResults.length >= acceptedFiles.length)
        // Appelle la fonction de rappel avec les résultats de lecture
        callback(readResults);
};

// Fonction pour charger les fichiers à partir d'un événement de drag-and-drop
var loadFiles = function(event, callback){
    // Empêche le comportement par défaut de l'événement
    event.preventDefault();

    // Réinitialise les tableaux
    droppedFiles = [];
    acceptedFiles = [];
    readResults = [];

    // Récupère les effets autorisés pour le drag-and-drop
    var allowedEffects = event.dataTransfer.effectAllowed;

    // Vérifie si tous les types de fichiers sont autorisés
    if (allowedEffects === "all"){
        // Récupère les fichiers de l'événement de drag-and-drop
        droppedFiles = event.dataTransfer.files;

        // Filtre les fichiers acceptés en fonction de leur type
        for(var i = 0; i < droppedFiles.length; i++){
            if(acceptedFileTypes.length <= 0 || acceptedFileTypes.indexOf(droppedFiles[i].type) >= 0)
                acceptedFiles.push(droppedFiles[i]);
        }

        // Lit chaque fichier accepté
        for(var i = 0; i < acceptedFiles.length; i++){
            var file = acceptedFiles[i];
            var fileReader = new FileReader();
            fileReader.fileName = file.name;
            fileReader.fileType = file.type;
            fileReader.filePath = file.path;

            // Fonction appelée lorsque la lecture du fichier est terminée
            fileReader.onload = function (event){
                var fileData = event.target.result;
                var fileName = this.fileName;
                var fileType = this.fileType;
                var filePath = this.filePath;
                // Ajoute les informations du fichier lu au tableau des résultats
                readResults.push({ name: fileName, path: filePath, type: fileType, data: fileData });
                // Appelle la fonction onFilesReady pour vérifier si tous les fichiers sont prêts
                onFilesReady(callback);
            };

            // Lit le fichier en fonction de son type
            if(/^image/.test(file.type) || /^application\/pdf/.test(file.type))
                fileReader.readAsDataURL(file);
            else if(/^text/.test(file.type) || file.type === 'application/json')
                fileReader.readAsText(file);
        }
    }
};

Et l'explication fournie par le chat.

  1. dr -> droppedFiles : Tableau pour stocker les fichiers obtenus à partir de l'événement de drag-and-drop.
  2. acc -> acceptedFiles : Tableau pour stocker les fichiers acceptés en fonction de leur type.
  3. re -> readResults : Tableau pour stocker les résultats de la lecture des fichiers.
  4. onReady -> onFilesReady : Fonction appelée lorsque les fichiers sont prêts à être traités.
  5. loadFile -> loadFiles : Fonction pour charger les fichiers à partir d'un événement de drag-and-drop.
  6. ea -> allowedEffects : Récupère les effets autorisés pour le drag-and-drop.
  7. f -> file : Représente un fichier accepté.
  8. r -> fileReader : Instance de FileReader pour lire le fichier.
  9. fd -> fileData : Données du fichier lu.
  10. fn -> fileName : Nom du fichier.
  11. ft -> fileType : Type du fichier.
  12. fp -> filePath : Chemin du fichier.

Le résultat est satisfaisant : le nouveau code est plus lisible, documenté et fonctionne.

⚠️

Le chat bot est donc un outil très intéressant pour ce genre de tâche fastidieuse et tout sauf fun.
Cependant, si vous lui demandez de modifier le code, restez très vigilants.
Ne copiez pas aveuglément le code fourni par le chat bot car celui-ci peut se tromper et insérer des erreurs. Relisez bien le code obtenu.
Ne traitez que des portions de code relativement petites. Les résultats n'en seront que meilleurs.

⚠️

Mon second avertissement concerne les portions de code confidentielles.
Vos prompts sont envoyés vers un serveur pour pouvoir être traîtés par l'IA du chat bot. Vous ne savez pas ce que ces services font de vos prompts, s'ils sont stockés, si leur accès est sécurisé correctement ou s'ils sont utilisés pour entraîner leurs IA ce qui impliquerait que le contenu de vos prompt pourrait apparaitre dans les réponses du chat bot aux autres utilisateurs.
Si vous travaillez sur des projets confidentiels, le plus prudent reste de faire le travail de documentation et de réusinage à la main.

Je vous ai montré cette méthode pour commenter et documenter du code, mais vous pouvez vous en servir aussi pour générer toute sorte de document reloud à rédiger comme la documentation technique ou encore la documentation utilisateur. Dans tous les cas, ne négligez jamais de contrôler le contenu que le chat bot a produit et de le corriger si besoin.

Elaborer un cahier des charges et une architecture

Maintenant, on va aborder ce que j'appelle la technique du catalyseur cognitif ! 🧠

Je m'explique.

Il y a un phénomène qui a forcément du vous arriver un jour.
En gros, vous avez un problème que vous n'arrivez pas à résoudre. Vous en parlez à un proche ou à un collègue et puis... POUF ! 💡 Avant même que votre interlocuteur ait ouvert la bouche, vous trouvez la solution !
En fait, le simple fait d'avoir essayé de formuler votre problème avec des mots a amené votre cerveau à réfléchir autrement et à trouver la solution.

Et bien vous pouvez faire pareil avec un chat bot. Le simple fait d'essayer de formuler votre problème dans un prompt peut vous apporter de nouvelles idées avant même de l'envoyer au chat bot.

Et même si ce genre de phénomène spectaculaire n'est pas systématique, le fait d'avoir un interlocuteur qui "écoute" vos idées et vous pose des questions vous aide à aborder un problème sous une perspective nouvelle et à avoir de nouvelles idées ou étoffer celles que vous avez déjà.

Le fait est que vos proches et vos collègues ne sont pas disponibles 24h/24 alors que le chat bot l'est. C'est bien aussi quand vous êtes introverti et que les interactions sociales vous réclament un effort considérable. 🤪

Alors comment peut-on exploiter cette approche pour du développement logiciel ?

  • Pour brainstormer et mettre de l'ordre dans ses idées
  • Pour générer un cahier des charges
  • Pour élaborer une architecture pour le projet

Il n'est, encore une fois, pas question de générer du code ici.

On va travailler sur un petit projet de jeu de morpion en guise d'exemple.
L'approche naïve consiste à commencer à solliciter le chat bot avec un prompt très simple de ce type.

  Peux-tu m'aider à développer un jeu de morpion ? 

Voici la réponse du Chat de Mistral.

Bien sûr ! Développer un jeu de morpion (ou tic-tac-toe en anglais) est un excellent projet pour apprendre les bases de la programmation. Voici un guide étape par étape pour créer un jeu de morpion simple en Python.

Étape 1 : Initialiser le plateau de jeu

Nous allons commencer par créer une fonction pour initialiser le plateau de jeu. Le plateau sera une liste de listes, représentant une grille 3x3.

def initialiser_plateau():
    return [[' ' for _ in range(3)] for _ in range(3)]
  
Étape 2 : Afficher le plateau de jeu

Ensuite, nous allons créer une fonction pour afficher le plateau de jeu de manière lisible.

def afficher_plateau(plateau):
    for ligne in plateau:
        print('|'.join(ligne))
        print('-' * 5)
  
Étape 3 : Vérifier les conditions de victoire

Nous devons vérifier si un joueur a gagné après chaque tour. Nous allons créer une fonction pour cela.

def verifier_victoire(plateau, joueur):
    # Vérifier les lignes et les colonnes
    for i in range(3):
        if all([plateau[i][j] == joueur for j in range(3)]) or all([plateau[j][i] == joueur for j in range(3)]):
            return True
    # Vérifier les diagonales
    if plateau[0][0] == plateau[1][1] == plateau[2][2] == joueur or plateau[0][2] == plateau[1][1] == plateau[2][0] == joueur:
        return True
    return False
  
Étape 4 : Jouer un tour
def jouer_tour(plateau, joueur):
    while True:
        try:
            ligne = int(input(f"Joueur {joueur}, entrez le numéro de la ligne (0, 1, 2) : "))
            colonne = int(input(f"Joueur {joueur}, entrez le numéro de la colonne (0, 1, 2) : "))
            if plateau[ligne][colonne] == ' ':
                plateau[ligne][colonne] = joueur
                break
            else:
                print("Cette case est déjà occupée. Réessayez.")
        except (ValueError, IndexError):
            print("Entrée invalide. Réessayez.")
  
Étape 5 : Boucle principale du jeu

Enfin, nous allons créer la boucle principale du jeu qui alterne les tours entre les deux joueurs et vérifie les conditions de victoire ou de match nul.

def jeu_morpion():
    plateau = initialiser_plateau()
    joueur_actuel = 'X'
    tours = 0

    while True:
        afficher_plateau(plateau)
        jouer_tour(plateau, joueur_actuel)
        tours += 1

        if verifier_victoire(plateau, joueur_actuel):
            afficher_plateau(plateau)
            print(f"Le joueur {joueur_actuel} a gagné !")
            break

        if tours == 9:
            afficher_plateau(plateau)
            print("Match nul !")
            break

        joueur_actuel = 'O' if joueur_actuel == 'X' else 'X'

# Lancer le jeu
jeu_morpion()
  
Explication du code
  1. Initialiser le plateau : Crée une grille 3x3 vide.
  2. Afficher le plateau : Affiche la grille de manière lisible.
  3. Vérifier la victoire : Vérifie si un joueur a aligné trois symboles sur une ligne, une colonne ou une diagonale.
  4. Jouer un tour : Permet à un joueur de choisir une case vide et y place son symbole.
  5. Boucle principale : Alterne les tours entre les joueurs, vérifie les conditions de victoire ou de match nul, et affiche le résultat final.

Vous pouvez copier ce code dans un fichier Python et l'exécuter pour jouer au morpion. Si vous avez des questions ou besoin de modifications, n'hésitez pas à demander !

Le problème de ce genre de prompt c'est, d'une part, que vous obtenez une réponse à côté de la plaque parce que quand vous vous lancez dans ce genre de projet, vous avez forcément une idée un peu plus précise que ça de ce que vous voulez faire, et d'autre part, que vous allez devoir consommer beaucoup plus de prompts pour rectifier le tir. C'est du temps et des crédits de perdus.

Qu'est-ce qui ne va pas dans la réponse du Chat ?
Et bien il vous fournit directement du code (ça ne sera pas le cas pour un projet de grande taille, il vous posera plutôt une série de questions) alors qu'on voudrait un cahier des charges et qu'il part immédiatement du principe que vous voulez écrire un programme python qui se joue en console.

Genre, ça :

Capture du jeu de morpion en console

Bon, il fonctionne, mais on aurait bien aimé une 'tite interface graphique sympa et une 'tite IA pour y jouer seul. Si c'est pas trop demander.🥺

Note : J'ai aussi fait un test avec Chat GPT pour comparer. Alors lui me propose un code Javascript sans explication ni commentaire et surtout il utilise cette usine à gaz de React pour 9 malheureuses cases et un bouton. 😑

On va donc reprendre depuis zéro. Avant de solliciter le Chat, on va déjà noter dans un coin toutes les idées qu'on a déjà et qu'on compilera dans un unique gros prompt pour que le Chat nous donne une réponse plus pertinente.

On va faire une liste des idées qu'on a pour ce projet pour que le premier prompt soit aussi complet que possible.

  • Un jeu de morpion à jouer en solo contre l'ordinateur, ce qui implique une ia
  • Une interface graphique conviviale
  • Codé en Javasrcipt pour y jouer dans un navigateur web
  • Pas de librairie ni de framework type React ou Angular (c'est comme utiliser un bazooka pour tuer une mouche)

Je pense que ça devrait suffir. Ça reste un projet simple.

Maintenant, construisons notre prompt.

  Je souhaite développer un jeu de morpion. 
  Il s'agira d'un jeu solo contre l'ordinateur. Il faudra donc développer une IA qui affrontera le joueur.
  Le jeu se jouera dans une interface web conviviale.
  Le langage utilisé sera le Javascript, sans librairie ni framework tier.
  
  Joue le rôle d'un chef de projet logiciel expérimenté pour m'aider à rédiger le cahier des charges de ce jeu.
  Pose moi des questions et complète le cahier des charges avec mes réponses.

Voici la réponse du Chat de Mistral.

Bien sûr, je vais vous aider à rédiger un cahier des charges complet pour votre jeu de morpion. Voici quelques questions pour commencer :

1. Objectifs du Projet
  • Quel est l'objectif principal de ce projet ?
    • Développer un jeu de morpion solo contre une IA avec une interface web conviviale.
2. Fonctionnalités Principales
  • Quelles sont les fonctionnalités principales que vous souhaitez inclure ?
    • Interface utilisateur intuitive pour jouer au morpion.
    • IA capable de jouer contre le joueur humain.
    • Gestion des tours de jeu.
    • Détection des conditions de victoire, de défaite et de match nul.
    • Option pour recommencer une partie.
3. Interface Utilisateur
  • Comment imaginez-vous l'interface utilisateur ?
    • Une grille 3x3 pour le jeu de morpion.
    • Boutons pour recommencer la partie.
    • Affichage du score (nombre de victoires, défaites, matchs nuls).
    • Indicateur visuel pour montrer quel joueur doit jouer.
4. IA
  • Quel niveau de difficulté souhaitez-vous pour l'IA ?
    • Plusieurs niveaux de difficulté (facile, moyen, difficile).
    • L'IA doit être capable de gagner, perdre ou faire match nul de manière réaliste.
5. Technologies Utilisées
  • Quelles technologies souhaitez-vous utiliser ?
    • JavaScript pur (sans librairies ni frameworks tiers).
    • HTML et CSS pour l'interface utilisateur.
6. Performances et Compatibilité
  • Quelles sont les exigences en termes de performances et de compatibilité ?
    • Le jeu doit être fluide et réactif.
    • Compatibilité avec les navigateurs modernes (Chrome, Firefox, Safari, Edge).
7. Tests et Validation
  • Comment souhaitez-vous tester et valider le jeu ?
    • Tests unitaires pour les fonctionnalités principales.
    • Tests utilisateurs pour valider l'expérience de jeu.
8. Livrables
  • Quels sont les livrables attendus ?
    • Code source du jeu.
    • Documentation technique.
    • Guide utilisateur.
9. Planning
  • Quel est le planning prévisionnel pour ce projet ?
    • Phase de conception : 1 semaine.
    • Développement : 3 semaines.
    • Tests et validation : 1 semaine.
    • Déploiement : 1 semaine.
10. Budget
  • Quel est le budget alloué à ce projet ?
    • À définir en fonction des ressources disponibles.
11. Équipe Projet
  • Qui sera impliqué dans le projet ?
    • Développeurs front-end.
    • Testeurs.
    • Chef de projet.
12. Maintenance et Évolutions
  • Quelles sont les prévisions pour la maintenance et les évolutions futures ?
    • Corrections de bugs post-lancement.
    • Ajout de nouvelles fonctionnalités (par exemple, mode multijoueur en ligne).
Cahier des Charges
1. Introduction

Le projet consiste à développer un jeu de morpion solo contre une IA, avec une interface web conviviale. Le jeu sera développé en JavaScript pur, sans utilisation de librairies ou frameworks tiers.

2. Objectifs
  • Créer un jeu de morpion jouable en solo contre une IA.
  • Offrir une expérience utilisateur fluide et intuitive.
  • Développer une IA capable de jouer à différents niveaux de difficulté.
3. Fonctionnalités
  • Interface Utilisateur :

    • Grille de jeu 3x3.
    • Boutons pour recommencer la partie.
    • Affichage du score.
    • Indicateur visuel pour le tour de jeu.
  • IA :

    • Niveaux de difficulté : facile, moyen, difficile.
    • Capacité à gagner, perdre ou faire match nul de manière réaliste.
  • Gestion des Tours :

    • Alternance des tours entre le joueur humain et l'IA.
  • Conditions de Fin de Partie :

    • Détection des conditions de victoire, défaite et match nul.
4. Technologies
  • JavaScript pur.
  • HTML et CSS pour l'interface utilisateur.
5. Performances et Compatibilité
  • Fluidité et réactivité du jeu.
  • Compatibilité avec les navigateurs modernes.
6. Tests et Validation
  • Tests unitaires pour les fonctionnalités principales.
  • Tests utilisateurs pour valider l'expérience de jeu.
7. Livrables
  • Code source du jeu.
  • Documentation technique.
  • Guide utilisateur.
8. Planning
  • Conception : 1 semaine.
  • Développement : 3 semaines.
  • Tests et validation : 1 semaine.
  • Déploiement : 1 semaine.
9. Budget
  • À définir en fonction des ressources disponibles.
10. Équipe Projet
  • Développeurs front-end.
  • Testeurs.
  • Chef de projet.
11. Maintenance et Évolutions
  • Corrections de bugs post-lancement.
  • Ajout de nouvelles fonctionnalités (mode multijoueur en ligne).

N'hésitez pas à ajouter ou modifier des éléments en fonction de vos besoins spécifiques.

Vous noterez qu'au travers de ses questions il apporte beaucoup de suggestions pour préciser le projet.

On va répondre à chaque question au travers d'un unique prompt.

  Réponse à la question 2 : tes propositions sont satisfaisantes
  Réponse à la question 3 : tes propositions sont satisfaisantes
  Réponse à la question 4 : un seul niveau de difficulté suffira pour la première version
  Réponse à la question 5 : HTML, CSS et Javascript pur
  Réponse à la question 6 : le jeu doit être compatible aussi bien avec des navigateurs modernes qu'avec des navigateurs anciens (10 ou 15 ans d'âge)
  Réponse à la question 7 : les tests utilisateurs suffiront pour ce projet
  Réponse à la question 8 : le code source suffira comme livrable, l'interface devra être suffisamment claire pour être compréhensible sans guide utilisateur
  Réponse à la question 9 : le projet complet doit pouvoir se terminer en 2 jours
  Réponse à la question 11 : un unique développeur devrait suffir pour ce projet, les tests seront menés par les joueurs eux-même
  Réponse à la question 12 : on pourra ajouter plusieurs niveau de difficulté et ajouter un mode multijoueur en ligne
  
  Peux-tu mettre à jour le cahier des charges avec mes réponses ? 

Voici la réponse du Chat de Mistral.

Bien sûr, voici le cahier des charges mis à jour avec vos réponses :

Cahier des Charges
1. Introduction

Le projet consiste à développer un jeu de morpion solo contre une IA, avec une interface web conviviale. Le jeu sera développé en JavaScript pur, sans utilisation de librairies ou frameworks tiers.

2. Objectifs
  • Créer un jeu de morpion jouable en solo contre une IA.
  • Offrir une expérience utilisateur fluide et intuitive.
  • Développer une IA capable de jouer à un niveau de difficulté standard.
3. Fonctionnalités
  • Interface Utilisateur :

    • Grille de jeu 3x3.
    • Boutons pour recommencer la partie.
    • Affichage du score.
    • Indicateur visuel pour le tour de jeu.
  • IA :

    • Un niveau de difficulté standard.
    • Capacité à gagner, perdre ou faire match nul de manière réaliste.
  • Gestion des Tours :

    • Alternance des tours entre le joueur humain et l'IA.
  • Conditions de Fin de Partie :

    • Détection des conditions de victoire, défaite et match nul.
4. Technologies
  • JavaScript pur.
  • HTML et CSS pour l'interface utilisateur.
5. Performances et Compatibilité
  • Fluidité et réactivité du jeu.
  • Compatibilité avec les navigateurs modernes et anciens (jusqu'à 10 ou 15 ans d'âge).
6. Tests et Validation
  • Tests utilisateurs pour valider l'expérience de jeu.
7. Livrables
  • Code source du jeu.
8. Planning
  • Le projet complet doit être terminé en 2 jours.
9. Équipe Projet
  • Un développeur unique.
  • Les tests seront menés par les joueurs eux-mêmes.
10. Maintenance et Évolutions
  • Ajout de plusieurs niveaux de difficulté.
  • Ajout d'un mode multijoueur en ligne.

N'hésitez pas à me faire part de toute autre modification ou ajout que vous souhaiteriez apporter.

Voilà pour l'exemple de cahier des charges.
Le chat bot se montre être une secrétaire efficace. 😜

Maintenant on peut aller plus loin et tirer une architecture logicielle de ce cahier des charges.

Voici mon prompt.

  Maintenant tu es un développeur web professionnel habitué à coder en Javascript pur.
  Je voudrais que tu me proposes une architecture logicielle (liste des fichiers sources et des fonctions sans les implémenter mais en les expliquant) qui réponde à ce cahier des charges.

Voici la réponse du Chat.
J'ai eu beau lui demander de ne pas implémenter les fonctions, il n'a pas pu s'empêcher de me proposer un code complet. Mais ça, je pense que c'est parce que le projet d'exemple est vraiment trop petit.
Ceci dit, avant de me balancer le code, il me propose bien une description de l'architecture du projet.

Voici une proposition d'architecture logicielle pour le jeu de morpion en JavaScript pur, répondant au cahier des charges. L'architecture sera composée de plusieurs fichiers et fonctions pour structurer le code de manière claire et maintenable.

Architecture Logicielle
1. index.html
  • Description : Fichier HTML principal contenant la structure de base de l'interface utilisateur.
  • Contenu :
    • Balise <div> pour la grille de jeu.
    • Balise <button> pour recommencer la partie.
    • Balise <div> pour afficher le score.
    • Balise <div> pour indiquer quel joueur doit jouer.
2. styles.css
  • Description : Fichier CSS pour styliser l'interface utilisateur.
  • Contenu :
    • Styles pour la grille de jeu.
    • Styles pour les boutons et les indicateurs.
    • Styles pour les messages de victoire, défaite et match nul.
3. main.js
  • Description : Fichier JavaScript principal contenant la logique du jeu.
  • Fonctions :
    • initializeGame() : Initialise le jeu en réinitialisant la grille et les scores.
    • handleCellClick(event) : Gère le clic sur une cellule de la grille.
    • checkWinner() : Vérifie si un joueur a gagné.
    • checkDraw() : Vérifie si la partie est un match nul.
    • switchPlayer() : Change le tour de jeu entre le joueur humain et l'IA.
    • aiMove() : Effectue un mouvement pour l'IA.
    • updateScore(winner) : Met à jour le score en fonction du gagnant.
    • displayMessage(message) : Affiche un message à l'utilisateur (victoire, défaite, match nul).
4. ai.js
  • Description : Fichier JavaScript contenant la logique de l'IA.
  • Fonctions :
    • getBestMove() : Détermine le meilleur mouvement pour l'IA.
    • minimax(board, depth, isMaximizing) : Algorithme Minimax pour évaluer les mouvements possibles.
    • evaluateBoard(board) : Évalue l'état actuel de la grille pour déterminer le score.
Exemple de Structure des Fichiers
index.html
<!DOCTYPE html>
<html lang="fr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Jeu de Morpion</title>
    <link rel="stylesheet" href="styles.css">
</head>
<body>
    <div id="game-board"></div>
    <button id="restart-button">Recommencer</button>
    <div id="score-display"></div>
    <div id="turn-indicator"></div>
    <script src="main.js"></script>
    <script src="ai.js"></script>
</body>
</html>
	
styles.css
/* Styles pour la grille de jeu */
#game-board {
    display: grid;
    grid-template-columns: repeat(3, 100px);
    grid-template-rows: repeat(3, 100px);
    gap: 5px;
}

/* Styles pour les cellules de la grille */
.cell {
    width: 100px;
    height: 100px;
    background-color: #f0f0f0;
    display: flex;
    justify-content: center;
    align-items: center;
    font-size: 24px;
    cursor: pointer;
}

/* Styles pour les boutons et indicateurs */
#restart-button {
    margin-top: 20px;
    padding: 10px 20px;
    font-size: 16px;
}

#score-display, #turn-indicator {
    margin-top: 10px;
    font-size: 18px;
}
	
main.js
// Variables globales
let board = ['', '', '', '', '', '', '', '', ''];
let currentPlayer = 'X';
let scores = { X: 0, O: 0, draw: 0 };

// Initialise le jeu
function initializeGame() {
    board = ['', '', '', '', '', '', '', '', ''];
    currentPlayer = 'X';
    renderBoard();
    updateScoreDisplay();
    displayMessage('');
}

// Gère le clic sur une cellule
function handleCellClick(event) {
    const index = event.target.dataset.index;
    if (board[index] === '' && currentPlayer === 'X') {
        board[index] = currentPlayer;
        event.target.textContent = currentPlayer;
        if (checkWinner()) {
            updateScore('X');
            displayMessage('Vous avez gagné !');
            setTimeout(initializeGame, 2000);
        } else if (checkDraw()) {
            updateScore('draw');
            displayMessage('Match nul !');
            setTimeout(initializeGame, 2000);
        } else {
            switchPlayer();
            aiMove();
        }
    }
}

// Vérifie si un joueur a gagné
function checkWinner() {
    const winningCombinations = [
        [0, 1, 2], [3, 4, 5], [6, 7, 8],
        [0, 3, 6], [1, 4, 7], [2, 5, 8],
        [0, 4, 8], [2, 4, 6]
    ];
    return winningCombinations.some(combination => {
        return combination.every(index => board[index] === currentPlayer);
    });
}

// Vérifie si la partie est un match nul
function checkDraw() {
    return board.every(cell => cell !== '');
}

// Change le tour de jeu
function switchPlayer() {
    currentPlayer = currentPlayer === 'X' ? 'O' : 'X';
}

// Effectue un mouvement pour l'IA
function aiMove() {
    const bestMove = getBestMove();
    board[bestMove] = 'O';
    document.querySelector(`[data-index="${bestMove}"]`).textContent = 'O';
    if (checkWinner()) {
        updateScore('O');
        displayMessage('L\'IA a gagné !');
        setTimeout(initializeGame, 2000);
    } else if (checkDraw()) {
        updateScore('draw');
        displayMessage('Match nul !');
        setTimeout(initializeGame, 2000);
    } else {
        switchPlayer();
    }
}

// Met à jour le score
function updateScore(winner) {
    scores[winner]++;
    updateScoreDisplay();
}

// Affiche un message à l'utilisateur
function displayMessage(message) {
    document.getElementById('turn-indicator').textContent = message;
}

// Rend la grille de jeu
function renderBoard() {
    const gameBoard = document.getElementById('game-board');
    gameBoard.innerHTML = '';
    board.forEach((cell, index) => {
        const cellElement = document.createElement('div');
        cellElement.classList.add('cell');
        cellElement.dataset.index = index;
        cellElement.textContent = cell;
        cellElement.addEventListener('click', handleCellClick);
        gameBoard.appendChild(cellElement);
    });
}

// Met à jour l'affichage du score
function updateScoreDisplay() {
    document.getElementById('score-display').textContent = `Score : Vous ${scores.X} - IA ${scores.O} - Matchs nuls ${scores.draw}`;
}

// Initialise le jeu au chargement de la page
document.getElementById('restart-button').addEventListener('click', initializeGame);
initializeGame();
	
ai.js
// Détermine le meilleur mouvement pour l'IA
function getBestMove() {
    let bestScore = -Infinity;
    let bestMove;
    for (let i = 0; i < board.length; i++) {
        if (board[i] === '') {
            board[i] = 'O';
            let score = minimax(board, 0, false);
            board[i] = '';
            if (score > bestScore) {
                bestScore = score;
                bestMove = i;
            }
        }
    }
    return bestMove;
}

// Algorithme Minimax pour évaluer les mouvements possibles
function minimax(board, depth, isMaximizing) {
    if (checkWinner('O')) return 1;
    if (checkWinner('X')) return -1;
    if (checkDraw()) return 0;

    if (isMaximizing) {
        let bestScore = -Infinity;
        for (let i = 0; i < board.length; i++) {
            if (board[i] === '') {
                board[i] = 'O';
                let score = minimax(board, depth + 1, false);
                board[i] = '';
                bestScore = Math.max(score, bestScore);
            }
        }
        return bestScore;
    } else {
        let bestScore = Infinity;
        for (let i = 0; i < board.length; i++) {
            if (board[i] === '') {
                board[i] = 'X';
                let score = minimax(board, depth + 1, true);
                board[i] = '';
                bestScore = Math.min(score, bestScore);
            }
        }
        return bestScore;
    }
}

// Évalue l'état actuel de la grille pour déterminer le score
function evaluateBoard(board) {
    const winningCombinations = [
        [0, 1, 2], [3, 4, 5], [6, 7, 8],
        [0, 3, 6], [1, 4, 7], [2, 5, 8],
        [0, 4, 8], [2, 4, 6]
    ];
    for (let combination of winningCombinations) {
        const [a, b, c] = combination;
        if (board[a] === board[b] && board[b] === board[c]) {
            if (board[a] === 'O') return 1;
            if (board[a] === 'X') return -1;
        }
    }
    return 0;
}
	
Explications
  • index.html : Contient la structure HTML de base avec les éléments nécessaires pour afficher la grille de jeu, le bouton de redémarrage, le score et l'indicateur de tour.
  • styles.css : Définit les styles pour la grille de jeu, les cellules, les boutons et les indicateurs.
  • main.js : Contient la logique principale du jeu, y compris l'initialisation, la gestion des clics, la vérification des conditions de victoire et de match nul, et la gestion des tours.
  • ai.js : Contient la logique de l'IA, y compris l'algorithme Minimax pour déterminer le meilleur mouvement.

Cette architecture permet de séparer les préoccupations et de rendre le code plus maintenable et extensible.

J'ai testé le code proposé par curiosité. Il fonctionne, mais l'IA du jeu laisse clairement gagner le joueur. 😂

Je vous ai montré un exemple sur un tout petit projet pour lequel une telle démarche n'est, je pense, pas franchement pertinente, mais c'est pour présenter le principe rapidement dans cet article déjà bien assez long.
Je pense que je ferai un test sur un projet plus gros pour mieux mettre en valeur l'intéret de la méthode. Ça fera l'objet d'un article dédié.

⚠️

Mon avertissement de tout-à-l'heure s'applique toujours : restez prudents et ne saisissez pas d'informations confidentielles lorsque vous interagissez avec un chat bot.

Conclusion

L'IA est un outil à utiliser avec parcimonie.
À chaque fois que vous voulez ouvrir un chat bot, demandez vous s'il n'y a pas d'outil plus adapté pour exécuter votre tâche.
Outre le fait que les IA consomment beaucoup d'énergie dans une ère où on a besoin de l'économiser, à vouloir utiliser les chat bots pour tout, vous risquez de vous retrouver démunis lorsque vous n'y aurez pas accès pour une raison X ou Y (pas d'accès à Internet, panne, hausse du prix des abonnements...), mais aussi, vous risquez de perdre du temps au lieu d'en gagner à chercher un prompt qui vous fournisse une réponse satisfaisante.

En vérité, dans le monde du machine learning, on n'utilise le deep learning que lorsqu'on a épuisé toutes les autres solutions traditionnelles.
Les chat bots sont basé sur le deep learning et je pense qu'on devrait les utiliser avec la même approche : uniquement lorsqu'on a épuisé les autres solutions.

Si vous lisez ces lignes, il est très probable que vous soyez vous-même développeur (ou développeuse) au moins en hobby. Vous avez donc une compétence qui, par nature, sert à automatiser des tâches. Vous pouvez déployer des trésors d'inventivité pour créer des outils d'automatisation spécialisés et optimisés pour vos besoins.
Profitez de cette compétence. 🙂

Si vous cherchez une recette de crêpes, s'il vous plait, soyez raisonnables.

Rendez-vous sur votre site de cuisine préféré et faites y une recherche traditionnelle.

Vous aurez le même résultat tout aussi rapidement en consommant 10 fois moins d'énergie.

Voilà pour ce premier article de l'Antre d'une Geekette. J'espère qu'il vous a plu et je vous dis à bientôt pour un prochain article ! 😀

Longue vie et prospérité ! 🖖

Longue vie et prospérité. (Spock)