Solution de contournement pour les méthodes génériques
Fait partie de la section Programmation Orientée Objet du Journey GO de Coddy. Leçon 71 sur 107.
Go présente une limitation notable : vous ne pouvez pas définir de méthodes avec leurs propres paramètres de type sur une structure. Bien que les structures génériques fonctionnent très bien, l’ajout d’une méthode qui introduit un nouveau paramètre de type n’est pas autorisé.
Ce code ne se compilera pas :
type Box[T any] struct {
Value T
}
// ERROR: la méthode ne doit pas avoir de paramètres de type
func (b Box[T]) Convert[U any](fn func(T) U) U {
return fn(b.Value)
}La solution consiste à utiliser une fonction générique autonome plutôt qu’une méthode :
type Box[T any] struct {
Value T
}
// Méthode ordinaire - utilise le paramètre de type de la structure
func (b Box[T]) Get() T {
return b.Value
}
// Fonction autonome - peut avoir ses propres paramètres de type
func Convert[T, U any](b Box[T], fn func(T) U) U {
return fn(b.Value)
}
func main() {
intBox := Box[int]{Value: 42}
// Utiliser la méthode
fmt.Println(intBox.Get()) // 42
// Utiliser la fonction autonome
strResult := Convert(intBox, func(n int) string {
return fmt.Sprintf("Number: %d", n)
})
fmt.Println(strResult) // Number: 42
}Les méthodes des structures génériques peuvent toujours utiliser le paramètre de type de la structure T. La restriction s’applique uniquement à l’introduction de paramètres de type supplémentaires dans la signature de la méthode.
Lorsque vous avez besoin de cette flexibilité, une fonction générique qui prend la structure comme premier argument obtient le même résultat.
Défi
FacileConstruisons une boîte à outils de transformation de données qui illustre la solution de contournement pour contourner la limitation de Go concernant les méthodes génériques ! Puisque les méthodes ne peuvent pas introduire de nouveaux paramètres de type, vous allez créer des fonctions génériques autonomes qui offrent la même flexibilité.
Vous allez organiser votre code sur deux fichiers :
wrapper.go: définissez votre conteneur générique et vos fonctions de transformation.Créez une structure générique
Wrapper[T any]avec un seul champValuede typeT.Ajoutez une méthode
GetàWrapper[T]qui renvoie la valeur encapsulée. Cette méthode utilise le paramètre de type de la structure, ce qui est autorisé.Créez une fonction générique autonome
Transform[T, U any](w Wrapper[T], fn func(T) U) Uqui applique la fonction de transformation à la valeur du wrapper et renvoie le résultat. Cette fonction a besoin de son propre paramètre de typeUpour le type de sortie, c'est pourquoi elle doit être une fonction autonome plutôt qu'une méthode.Créez une autre fonction autonome
TransformToString[T any](w Wrapper[T]) stringqui convertit la valeur encapsulée en chaîne à l'aide defmt.Sprintf("%v", ...).main.go: illustrez le modèle de solution de contournement avec différentes transformations.Lisez un indicateur de type (
intoustring), puis lisez une valeur. Créez unWrapperdu type approprié.Pour une entrée
int: créez unWrapper[int], puis utilisezTransformpour doubler la valeur (en renvoyant un int), et utilisezTransformToStringpour obtenir une représentation sous forme de chaîne.Pour une entrée
string: créez unWrapper[string], puis utilisezTransformpour obtenir la longueur de la chaîne (en renvoyant un int), et utilisezTransformToStringpour obtenir la représentation sous forme de chaîne.Affichez les résultats dans ce format :
Original: [value] Transformed: [transformed value] As String: [string representation]
Les entrées suivantes seront fournies :
- Ligne 1 : indicateur de type (
intoustring) - Ligne 2 : la valeur
Par exemple, avec :
int
25Votre sortie doit être :
Original: 25
Transformed: 50
As String: 25Et avec :
string
Hello WorldVotre sortie doit être :
Original: Hello World
Transformed: 11
As String: Hello WorldL'idée essentielle est que Transform prend un Wrapper[T] comme premier argument et introduit un nouveau paramètre de type U pour le type de retour. Ce qui ne serait pas possible en tant que méthode de la structure. Ce modèle vous offre la flexibilité des transformations génériques tout en respectant les contraintes du système de types de Go.
Essayez vous-même
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Lire l'indicateur de type
typeIndicator, _ := reader.ReadString('\n')
typeIndicator = strings.TrimSpace(typeIndicator)
// Lire la valeur
value, _ := reader.ReadString('\n')
value = strings.TrimSpace(value)
if typeIndicator == "int" {
// Analyser la valeur entière
num, _ := strconv.Atoi(value)
// TODO: Créer un Wrapper[int] avec le nombre analysé
// TODO: Utiliser Transform pour doubler la valeur (indice : passer une fonction qui double)
// TODO: Utiliser TransformToString pour obtenir la représentation sous forme de chaîne
// TODO: Afficher les résultats dans le format requis :
// Original: [value]
// Transformed: [transformed value]
// As String: [string representation]
_ = num // Supprimez cette ligne lorsque vous utilisez num
} else if typeIndicator == "string" {
// TODO: Créer un Wrapper[string] avec la valeur
// TODO: Utilisez Transform pour obtenir la longueur de la chaîne (indice : passez une fonction qui retourne len())
// TODO: Utiliser TransformToString pour obtenir la représentation sous forme de chaîne
// TODO: Afficher les résultats dans le format requis :
// Original: [value]
// Transformed: [transformed value]
// As String: [string representation]
_ = value // Supprimez cette ligne lorsque vous utilisez value
}
}
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 workersEntraînez-vous par vous-même : Compilateur Go en ligne