Définition
Une interface est un ensemble de signatures de méthodes qu'une structure peut implémenter. Par conséquent, l'interface définit le comportement d’une structure.
Le but principal de l'interface consiste à ne fournir que des signatures de méthodes composées :
- Du nom de la méthode
- Des arguments d'entrée
- Des types de retour
Il appartient à la structure de déclarer les méthodes signées dans l’interface et de les implémenter
Démonstration step by step
Déclarer une interface
Pour déclarer une interface nous avons besoin du mot-clé type qui permet d'associer un nom à votre interface et du du mo-clé interface qui indique à votre compilateur qu'il s'agit d'une interface.
package main
import (
"fmt"
)
type Forme interface { // création de L'interface Forme
Air() float64 // signature de la méthode Air()
Perimetre() float64 // signature de la méthode Perimetre()
}
func main() {
var f Forme // Initialisation de l'interface Forme
fmt.Println(f)
}
Comme vous pouvez le voir dans une interface on ne met que des signatures de méthodes sans aucun attributs. Pour le moment ces méthodes ne sont pas encore utilisées car pour l'instant je vous montre juste comment déclarer une interface.
Résultat :
nil
D'après le résultat ci-dessus, nous pouvons voir que la valeur de notre interface est nil car il n'existe aucune structure qui implémente l'interface Forme.
Utilisation d'une interface
Notre interface Forme possède deux signatures :
- Air() qui retourne un type float64
- Perimetre() qui retourne un type float64
La structure qui va implémenter l'interface Forme doit obligatoirement implémenter les méthodes Air() et Perimetre() signées dans l'interface Forme.
package main
import (
"fmt"
)
type Forme interface {
Air() float64
Perimetre() float64
}
type Rectangle struct {
largeur float64
longueur float64
}
/*
Pour implémenter une interface dans Go, il suffit
d'implémente toutes les méthodes de l'interface. Ici on
implémente la méthode Air() de l'interface Forme.
*/
func (r Rectangle) Air() float64 {
return r.largeur * r.longueur
}
/*
On implémente la méthode Perimetre() de l'interface Forme
*/
func (r Rectangle) Perimetre() float64 {
return 2 * (r.largeur * r.longueur)
}
func main() {
var f Forme
f = Rectangle{5.0, 4.0} // affectation de la structure Rectangle à l'interface Forme
r := Rectangle{5.0, 4.0}
fmt.Println("Type de f :", f)
fmt.Printf("Valeur de f : %v\n", f)
fmt.Println("Air du rectangle r :", f.Air())
fmt.Println("f == r ? ", f == r)
}
Il est tout à fait possible d'affecter à notre interface Forme la structure Rectangle puisque la structure Rectangle implémente l'interface Forme.
Résultat :
Type de f : {5 4}
Valeur de f : {5 4}
Air du rectangle r : 20
f == r ? true
Sur le résultat on peut remarquer que f == r ? est à true car dans mon exemple f et r ont le même type et la même valeur.
Je vais intentionnellement supprimer la méthode Air() de la structure Rectangle pour ainsi vous prouvez qu'il est vraiment obligatoire d'implémenter toutes les méthodes de l'interface Forme.
package main
import (
"fmt"
)
type Forme interface {
Air() float64
Perimetre() float64
}
type Rectangle struct {
largeur float64
longueur float64
}
/* Pas de méthode Air */
func (r Rectangle) Perimetre() float64 {
return 2 * (r.largeur * r.longueur)
}
func main() {
var f Forme
f = Rectangle{5.0, 4.0}
r := Rectangle{5.0, 4.0}
fmt.Println("Type de f :", f)
fmt.Printf("Valeur de f : %v\n", f)
fmt.Println("Air du rectangle r :", f.Air())
fmt.Println("f == r ? ", f == r)
}
Erreur :
.cannot use Rectangle literal (type Rectangle) as type Forme in assignment:
Rectangle does not implement Forme (missing Air method)
invalid operation: f == r (mismatched types Forme and Rectangle)
L'erreur ci-dessus indique clairement que, pour implémenter une interface avec succès, vous devez implémenter toutes les méthodes déclarées par l'interface et notre compilateur nous le fait bien savoir on nous marquons qu'il manque la méthode Air() "missing Air method".
On va dans cet exemple créer deux structures Cercle et Rectangle qui implémente l'interface Forme :
package main
import (
"fmt"
"math"
)
type Forme interface {
Air() float64
Perimetre() float64
}
/*----------------Début Structure Rectangle----------------*/
type Rectangle struct {
largeur float64
longueur float64
}
func (r Rectangle) Air() float64 {
return r.largeur * r.longueur
}
func (r Rectangle) Perimetre() float64 {
return 2 * (r.largeur * r.longueur)
}
/*----------------Fin Structure Rectangle----------------*/
/*----------------Début Structure Cercle-----------------*/
type Cercle struct {
rayon float64
}
func (c Cercle) Air() float64 {
return math.Pi * c.rayon * c.rayon
}
func (c Cercle) Perimetre() float64 {
return 2 * math.Pi * c.rayon
}
/*----------------Fin Structure Cercle-------------------*/
//Fonction qui prend compte un paramètre de type Cerle ou Rectangle
func AirPerimetrePresentation(f Forme) {
fmt.Println("- Air :", f.Air())
fmt.Println("- Perimetre :", f.Perimetre())
}
func main() {
var r Forme = Rectangle{5.0, 4.0}
var c Forme = Cercle{5.0}
fmt.Println("Cercle :")
AirPerimetrePresentation(r)
fmt.Println("\nrectangle :")
AirPerimetrePresentation(c)
}
Résultat :
Cercle :
- Air : 20
- Perimetre : 40
rectangle :
- Air : 78.53981633974483
- Perimetre : 31.41592653589793
Le résultat ci-dessus nous montre que l'interface Forme peut être déclaré en tant que Cercle ou Rectangle
Conclusion
Les interfaces sont une sorte de contrat, ils permettent de créer des comportements génériques: si plusieurs structures doivent obéir à des comportements particuliers, alors on créé une interface décrivant ces comportements. Ces structures devront ainsi obéir strictement aux signatures de l'interface, sans quoi la compilation ne se fera pas.
Grâce à eux on peut obtenir un code beaucoup plus claire et facile à maintenir.
Espace commentaire
Écrire un commentaire
Rejoignez la discussion
Vous devez être connecté pour poster un message.
21 commentaires
Est ce que vous pouvez presenter un cours comme cela sur JAVA, C ou C++?
Content que ça serve. C'est l'essence même de Go : faire simple et robuste. N'oublie pas de tester tes implémentations avec des tests unitaires.
Top le tuto. Ça m'a aidé à refactorer mon code spaghetti en utilisant le polymorphisme des interfaces.
Oui, mais attention à la sémantique. Si tu implémentes sur
*Rectangle, ton interface ne sera valide que pour les pointeurs versRectangle. Exemple :Ici,
f = Rectangle{}ne marchera pas, il faudraf = &Rectangle{}.Peut-on implémenter une interface via un pointeur ?
Vérifie que ta structure implémente bien les méthodes avec les bons types de retour. Si ton interface attend
float64et que ta méthode renvoie unint, ça ne passera jamais.Mon IDE souligne en rouge
f = Rectangle{5.0, 4.0}. Il me dit que c'est pas le bon type.Parce que le but de l'interface est de garantir un comportement. Si tu appelles
f.Air()et que la méthode n'existe pas, ton prog crashe en runtime. Le compilo t'évite justement ce genre de catastrophe en prod.Question bête : pourquoi le compilateur râle si je n'implémente pas tout ? C'est pas plus simple de laisser passer ?
Oui, c'est ce qu'on appelle l'embedding d'interfaces. Tu peux composer une interface à partir d'autres plus petites. Ça garde ton code modulaire.
Merci pour le cours car je suis un initié dans la programmation avec le langage Goland
Est-ce qu'une interface peut hériter d'une autre interface ?
Exact, @5 a raison. Sans
import "math", impossible d'accéder aux constantes mathématiques. Vérifie ton header.T'as bien importé le package
mathen haut de ton fichier ? J'ai eu le même souci au début.J'essaie de compiler ton exemple avec le
Cerclemais j'ai un souci avecmath.Pi. Il me dit undefined.C'est une interface vide. Elle accepte n'importe quel type puisqu'aucune méthode n'est requise. C'est utile pour de la généricité forcée, mais à utiliser avec parcimonie pour éviter de perdre le typage fort.
Je bosse sur un projet existant et je vois des interfaces vides
interface{}partout. C'est quoi le délire ?Non, à ne jamais faire. Une interface ne contient que des signatures de méthodes. Si tu veux stocker des données, utilise une
struct. L'interface, c'est uniquement le contrat de comportement.J'ai une question de débutant : est-ce qu'on peut ajouter des attributs à l'interface ? Le code plante quand j'essaie.
C'est que tu as oublié une méthode. Comme expliqué, si tu rates ne serait-ce qu'une signature de l'interface
Forme, le compilo bloque. Vérifie bien queAir()etPerimetre()sont présentes dans ta struct.Tuto utile pour débuter. Par contre, j'ai une erreur
cannot use Rectangle literalau moment de l'assignation. J'ai pourtant bien défini ma structure.