Vue d’ensemble de la génération de code
Fait partie de la section Programmation Orientée Objet du Journey GO de Coddy. Leçon 84 sur 107.
La génération de code est une alternative à la réflexion qui crée du code source Go au moment de la compilation plutôt que d’inspecter les types lors de l’exécution. Cette approche vous offre la sécurité des types et de meilleures performances, tout en réduisant le code répétitif standard.
Go fournit la commande go generate pour automatiser la génération de code. Vous placez une directive de commentaire spéciale dans votre fichier source, puis l’exécution de go generate lance l’outil spécifié :
//go:generate stringer -type=Status
type Status int
const (
Pending Status = iota
Active
Completed
)L’exécution de go generate ./... invoque l’outil stringer, qui crée un nouveau fichier contenant une méthode String() pour le type Status. Le code généré est du code Go ordinaire qui est compilé avec votre programme.
Les outils courants de génération de code dans l’écosystème Go incluent stringer pour les méthodes de conversion d’énumérations en chaînes, mockgen pour créer des mocks de test à partir d’interfaces, et protoc pour générer du code à partir de définitions Protocol Buffer. De nombreux ORM et frameworks d’API utilisent également la génération pour créer des requêtes de base de données fortement typées ou du code client.
Le principal avantage par rapport à la réflexion est que le code généré est vérifié par le compilateur. Les erreurs sont détectées au moment de la compilation, et non à l’exécution. En contrepartie, une étape de compilation supplémentaire est nécessaire, ainsi que des fichiers générés à gérer dans votre dépôt. Pour les modèles de POO nécessitant des implémentations répétitives sur de nombreux types, la génération de code offre souvent le meilleur équilibre entre sécurité et commodité.
Défi
FacileConstruisons un générateur de code qui crée des méthodes String() pour des types semblables à des énumérations ! Même si nous ne pouvons pas exécuter un véritable go generate dans cet environnement, vous simulerez ce que font les outils de génération de code : produire programmatiquement du code source Go à partir de définitions de types.
Vous organiserez votre code sur deux fichiers :
generator.go: créez votre logique de génération de code.Construisez une structure
EnumTypequi représente une définition de type semblable à une énumération, avec unName(chaîne) et desValues(slice de chaînes représentant les noms des constantes).Créez une fonction
GenerateStringMethod(e EnumType) stringqui produit du code source Go pour une méthodeString(). Le code généré doit suivre ce modèle :func (e TypeName) String() string { switch e { case Value1: return "Value1" case Value2: return "Value2" default: return "Unknown" } }Créez également une fonction
GenerateConstants(e EnumType) stringqui produit les déclarations de constantes à l'aide deiota:const ( Value1 TypeName = iota Value2 Value3 )Enfin, créez
GenerateFullCode(e EnumType) string, qui combine les deux sorties avec un commentaire d'en-tête indiquant qu'il s'agit de code généré :// Code generated by enum generator. DO NOT EDIT. [constants] [string method]main.go: utilisez votre générateur pour produire du code pour différents types semblables à des énumérations.Lisez le nom du type d'énumération, puis lisez un nombre de valeurs suivi de chaque nom de valeur. Transmettez ces informations à votre générateur et affichez le code généré complet.
Les entrées suivantes seront fournies :
- Ligne 1 : nom du type d'énumération
- Ligne 2 : nombre de valeurs
- Lignes suivantes : chaque nom de valeur
Par exemple, avec :
Status
3
Pending
Active
CompletedVotre sortie doit être :
// Code generated by enum generator. DO NOT EDIT.
const (
Pending Status = iota
Active
Completed
)
func (e Status) String() string {
switch e {
case Pending:
return "Pending"
case Active:
return "Active"
case Completed:
return "Completed"
default:
return "Unknown"
}
}Et avec :
Priority
2
Low
HighVotre sortie doit être :
// Code generated by enum generator. DO NOT EDIT.
const (
Low Priority = iota
High
)
func (e Priority) String() string {
switch e {
case Low:
return "Low"
case High:
return "High"
default:
return "Unknown"
}
}Cet exercice illustre le concept fondamental qui se trouve derrière des outils comme stringer : générer du code répétitif, mais sûr du point de vue des types, au moment de la compilation plutôt que d'utiliser la réflexion à l'exécution. Votre générateur produit du code Go valide qui pourrait être enregistré dans un fichier et compilé avec le reste d'un projet.
Essayez vous-même
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Lire le nom du type enum
typeName, _ := reader.ReadString('\n')
typeName = strings.TrimSpace(typeName)
// Lire le nombre de valeurs
countStr, _ := reader.ReadString('\n')
count, _ := strconv.Atoi(strings.TrimSpace(countStr))
// Lire le nom de chaque valeur
values := make([]string, count)
for i := 0; i < count; i++ {
value, _ := reader.ReadString('\n')
values[i] = strings.TrimSpace(value)
}
// TODO: Créer un EnumType avec les données lues
// TODO: Générer et afficher le code complet en utilisant GenerateFullCode
}
Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Programmation Orientée Objet
1Fondamentaux de la POO en Go
Fichiers externesEspace de travail et modules GoPackages et importsNoms exportés et non exportésIntroduction à la POO en GoStructs comme classesDéfinir des méthodes sur des structsRécepteurs pointeurs ou par valeurInitialisation des structsFonctions constructeursRécapitulatif - Calculatrice simple4Interfaces
Introduction aux interfacesImplémentation impliciteL’interface comme contratInterface vide (any)Assertion de typeCommutation de typeComposition d’interfacesInterfaces Stringer et ErrorRécapitulatif - Calculateur de formes7Encapsulation
Champs exportés vs non exportésEncapsulation au niveau du packageMéthodes Getter et SetterDissimulation de l’information en GoRécapitulatif – Fiches d’étudiants10Génériques (Go 1.18+)
Introduction aux génériquesParamètres de typeContraintes de typeStructures génériquesSolution de contournement pour les méthodes génériquesRécapitulatif - Collection générique2Plongée approfondie dans les types et les structs
Types de base et compositesDéfinitions de types personnalisésTags de structStructs anonymesStructs imbriquésValeurs zéro et valeurs par défautRécapitulatif - Carnet de contacts5La composition plutôt que l’héritage
Pourquoi Go n’a pas d’héritageBases de l’inclusion de structsPromotion des méthodesInclure plusieurs structsInclusion ou agrégationMasquage des méthodes inclusesRécapitulatif : hiérarchie des employés8Gestion des erreurs et POO
L’interface errorTypes d’erreurs personnalisésEnrobage des erreurs (fmt.Errorf)Erreurs sentinelleserrors.Is() et errors.As()Panic, Defer et RecoverRécapitulatif - Analyseur de fichiers11Bibliothèque standard et POO
io.Reader et io.Writersort.InterfaceInterface fmt.Stringerencoding/json avec des structsInterface http.HandlerRécapitulatif - Modèles d’API REST14Modèles de conception – Partie 2
Modèle CommandeModèle AdaptateurModèle DécorateurModèle Méthode modèleModèle ÉtatModèle CompositeMiddleware comme décorateur3Pointeurs et mémoire
Notions de base des pointeurs en GoPointeurs vers des structuresPassage par valeur ou par référenceLa fonction new()Garbage collection en GoRécapitulatif - Constructeur de listes chaînées6Polymorphisme en Go
Polymorphisme via les interfacesDuck typing en GoRègles de satisfaction des interfacesCollections polymorphesInjection de dépendancesRécapitulatif – Processeur de paiements9Concurrence et POO
Bases des GoroutinesCanaux et communicationCanaux tamponnés ou non tamponnésInstruction selectsync.Mutex et sync.RWMutexsync.WaitGroupConception de structures thread-safeRécapitulatif - Pool de workers12Concepts avancés de la POO
Pattern des options fonctionnellesPattern Builder en GoChaînage de méthodesAlias de types vs définitionsBases de la réflexionVue d’ensemble de la génération de codeEntraînez-vous par vous-même : Compilateur Go en ligne