Leçons du module (1/5)
Interfaces : définir un comportement
Une interface dans Go est un ensemble de signatures de méthodes. Une valeur
implémenter l'interface si elle a toutes ces méthodes - rien
implements, rien extends, pas de journalisation explicite. C'est le
motif souvent appelé « typage structurel » ou « typage canard »
static » : le compilateur vérifie la conformité de la compilation, mais
basé sur la forme, pas sur des déclarations.
Déclarer une interface
type Greeter interface {
Greet() string
}Convention : noms se terminant par -er pour les interfaces uniques
méthode (Reader, Writer, Stringer, Closer). Les petites interfaces
(1 à 2 méthodes) sont idiomatiques : la bibliothèque standard regorge de
interfaces minuscules composées les unes des autres (io.ReadWriter,
io.ReadCloser, …)
Implémentation implicite
type Person struct{ Name string }
func (p Person) Greet() string {
return "ciao " + p.Name
}
// Person soddisfa Greeter perché ha il metodo richiesto.
var s Greeter = Person{Name: "Ada"}
fmt.Println(s.Greet())Aucun mot-clé. Si le type possède des méthodes, il peut être affecté à une variable de ce type d'interface. Indiquer.
Polymorphisme via l'interface
type Robot struct{ ID string }
func (r Robot) Greet() string { return "bip " + r.ID }
func greet(s Greeter) {
fmt.Println(s.Greet())
}
greet(Person{Name: "Ada"})
greet(Robot{ID: "R2"})greet accepte tout type qui satisfait à Greeter. C'est la voie à suivre
faites du polymorphisme : pas de hiérarchies, juste des capacités.
L'interface vierge
var x interface{} = 42
var y any = "ciao" // any è alias built-in di interface{}interface{} (ou any depuis Go 1.18) ne nécessite aucune méthode, donc
toute valeur le satisfait. Utile pour les conteneurs généraux, mais pour
l'extraction de la valeur concrète nécessite une assertion de type ou un commutateur de type
(prochains cours).
Ensemble de méthodes : valeur vs récepteur de pointeur (aperçu)
Rappelez-vous du module 5 :
- Les méthodes récepteur de valeur font partie de l'ensemble de méthodes de
Tet*T. - Les méthodes du récepteur de pointeur sont UNIQUEMENT dans l'ensemble de méthodes de
*T.
Conséquence pratique :
type Counter struct{ N int }
func (c *Counter) Inc() { c.N++ }
type Incrementer interface { Inc() }
var i Incrementer = &Counter{} // OK: *Counter ha Inc()
// var j Incrementer = Counter{} // ERRORE: Counter (valore) NOLorsqu'une méthode possède un récepteur de pointeur, vous devez attribuer un pointeur au variable d'interface.
Essayez-le vous-même
Définissez une interface Greeter avec une seule méthode de chaîne Greet().
Afficher l'indice
Syntaxe : `type Name interface { Method(args) return }`.
Solution disponible après 3 tentatives
Implémentez Greeter sur un nouveau type de robot avec un champ ID de chaîne ; la méthode renvoie 'bip' + ID.
Afficher l'indice
Rien `implements` : définissez simplement la méthode avec la bonne signature.
Solution disponible après 3 tentatives
Comment déclarer que le type P implémente l'interface Greeter ?
type P struct{}
// ?Récapitulatif
- Interface = ensemble de signatures de méthode ; non
implements. - Implémentation implicite : il suffit d'avoir les méthodes de la bonne signature.
- Idiomatique : petites interfaces (1 à 2 méthodes), noms en
-er. interface{}/any: accepte n'importe quelle valeur, perd la sécurité de type.- Ensemble de méthodes : récepteur pointeur → seul
*Tsatisfait l'interface. - Go polymorphisme : paramètres de type interface, pas de hiérarchies.