Interfaces du langage Go
ci-dessous :
La conception des interfaces de Go est simple mais puissante, et constitue un outil important pour réaliser le polymorphisme et le découplage.
Les interfaces nous permettent de lier différents types à un ensemble de méthodes communes, ce qui permet le polymorphisme et une conception flexible.
Caractéristiques des interfaces
Implémentation implicite:
- Go n'a pas de mot-clé pour déclarer explicitement qu'un type implémente une interface.
- Tant qu'un type implémente toutes les méthodes requises par une interface, ce type est automatiquement considéré comme implémentant cette interface.
Variables de type interface:
- Une variable d'interface peut stocker n'importe quelle valeur qui implémente l'interface.
- Une variable d'interface se compose en fait de deux parties :
- Type dynamique: stocke le type réel de la valeur.
- Valeur dynamique: stocke la valeur concrète.
Interface de valeur zéro:
- La valeur zéro d'une interface est
nil。 - La valeur d'une variable d'interface non initialisée est
nil, et elle ne contient aucun type dynamique ni aucune valeur.
Interface vide:
- Définie comme
interface{}, peut représenter n'importe quel type.
Utilisations courantes des interfaces
- PolymorphismeDes types différents qui implémentent la même interface obtiennent un comportement polymorphe.
- DécouplageDéfinir des dépendances via des interfaces réduit le couplage entre les modules.
- Généralisation: Utilisation de l'interface vide
interface{}représente n'importe quel type.
Définition et implémentation d'interface
Les définitions d'interface utilisent le mot-cléinterface, qui contient des déclarations de méthodes.
Exemple
type interface_name interface {
method_name1 [return_type]
method_name2 [return_type]
method_name3 [return_type]
...
method_namen [return_type]
}
/* Définir la structure */
type struct_name struct {
/* variables */
}
/* Implémenter les méthodes de l'interface */
func (struct_name_variable struct_name) method_name1() [return_type] {
/* Implémentation de la méthode */
}
...
func (struct_name_variable struct_name) method_namen() [return_type] {
/* Implémentation de la méthode*/
}
Définir une interface simple :
type Shape interface {
Area() float64
Perimeter() float64
}Shapeest une interface qui définit deux méthodes :AreaetPerimeter。- Tout type qui implémente ces deux méthodes est considéré comme ayant implémenté l'interface.
ShapeInterface.
Implémenter l'interface :Les types implémentent une interface en implémentant toutes les méthodes requises par l'interface.
Exemple
import (
"fmt"
"math"
)
// Définir l'interface
type Shape interface {
Area() float64
Perimeter() float64
}
// Définir une structure
type Circle struct {
Radius float64
}
// Circle implémente l'interface Shape
func (c Circle) Area() float64 {
return math.Pi * c.Radius * c.Radius
}
func (c Circle) Perimeter() float64 {
return 2 * math.Pi * c.Radius
}
func main() {
c := Circle{Radius: 5}
var s Shape = c // Les variables d'interface peuvent stocker des types qui implémentent l'interface
fmt.Println("Area:", s.Area())
fmt.Println("Perimeter:", s.Perimeter())
}
Exécutez le code ci-dessus, la sortie est :
Area: 78.53981633974483 Perimeter: 31.41592653589793
Interface vide
Interface videinterface{}Il s'agit d'une interface spéciale en Go, représentant le sur-ensemble de tous les types.
- Tout type implémente l'interface vide.
- Il est souvent utilisé dans des scénarios qui nécessitent de stocker des données de tout type, tels que les conteneurs génériques, les paramètres génériques, etc.
Exemple
import "fmt"
func printValue(val interface{}) {
fmt.Printf("Value: %v, Type: %T\n", val, val)
}
func main() {
printValue(42) // int
printValue("hello") // string
printValue(3.14) // float64
printValue([]int{1, 2}) // slice
}
Exécutez le code ci-dessus, la sortie est :
Value: 42, Type: int Value: hello, Type: string Value: 3.14, Type: float64 Value: [1 2], Type: []int
Assertion de type
L'assertion de type est utilisée pour extraire la valeur sous-jacente d'un type d'interface.
Syntaxe de base :
value := iface.(Type)
ifaceest une variable d'interface.Typeest le type concret à asserter.- Si le type ne correspond pas, cela déclenche
panic。
Exemple
import "fmt"
func main() {
var i interface{} = "hello"
str := i.(string) // Assertion de type
fmt.Println(str) // Sortie : hello
}
assertion de type avec vérification
Pour éviter un panic, une assertion de type vérifiée peut être utilisée :
value, ok := iface.(Type)
okIl s'agit d'une valeur booléenne indiquant si l'assertion a réussi.- Si l'assertion échoue,
valueest la valeur zéro,okestfalse。
Exemple
import "fmt"
func main() {
var i interface{} = 42
if str, ok := i.(string); ok {
fmt.Println("Chaîne :", str)
} else {
fmt.Println("Pas une chaîne")
}
}
Exécutez le code ci-dessus, la sortie est :
Not a string
switch de type
Le switch de type est une construction syntaxique en Go utilisée pour exécuter une logique différente en fonction du type concret d'une variable d'interface.
Exemple
import "fmt"
func printType(val interface{}) {
switch v := val.(type) {
case int:
fmt.Println("Entier :", v)
case string:
fmt.Println("Chaîne :", v)
case float64:
fmt.Println("Float:", v)
default:
fmt.Println("Unknown type")
}
}
func main() {
printType(42)
printType("hello")
printType(3.14)
printType([]int{1, 2, 3})
}
Exécutez le code ci-dessus, la sortie est :
Integer: 42 String: hello Float: 3.14 Unknown type
Composition d'interfaces
Les interfaces peuvent décrire des comportements plus complexes par l'imbrication et la composition.
Exemple
import "fmt"
type Reader interface {
Read() string
}
type Writer interface {
Write(data string)
}
type ReadWriter interface {
Reader
Writer
}
type File struct{}
func (f File) Read() string {
return "Lecture des données"
}
func (f File) Write(data string) {
fmt.Println("Écriture des données :", data)
}
func main() {
var rw ReadWriter = File{}
fmt.Println(rw.Read())
rw.Write("Bonjour, Go !")
}
valeur dynamique et type dynamique
Une variable d'interface contient en réalité deux parties :
- Type dynamique: le type concret stocké dans la variable d'interface.
- Valeur dynamique: la valeur du type concret.
Exemple de valeur dynamique et de type dynamique :
Exemple
import "fmt"
func main() {
var i interface{} = 42
fmt.Printf("Type dynamique : %T, Valeur dynamique : %v\n", i, i)
}
Exécutez le code ci-dessus, la sortie est :
Dynamic type: int, Dynamic value: 42
Valeur zéro d'interface
La valeur zéro d'une interface est nil.
Lorsque le type dynamique et la valeur dynamique d'une variable d'interface sont tous deux nil, la variable d'interface est nil.
Exemple de valeur zéro d'interface :
Exemple
import "fmt"
func main() {
var i interface{}
fmt.Println(i == nil) // Sortie : true
}
Exemples pratiques
Les deux exemples suivants démontrent l'utilisation des interfaces :
Exemple 1
import (
"fmt"
)
type Phone interface {
call()
}
type NokiaPhone struct {
}
func (nokiaPhone NokiaPhone) call() {
fmt.Println("Je suis Nokia, je peux vous appeler !")
}
type IPhone struct {
}
func (iPhone IPhone) call() {
fmt.Println("Je suis iPhone, je peux vous appeler !")
}
func main() {
var phone Phone
phone = new(NokiaPhone)
phone.call()
phone = new(IPhone)
phone.call()
}
Dans l'exemple ci-dessus, nous avons défini une interface.Phone, l'interface contient une méthodecall(). Ensuite, nousmainDans la fonction, une variable de typePhoneest définie, et des valeurs lui sont respectivement assignées commeNokiaPhoneetIPhone. Ensuite, appelez la méthodecall(), la sortie est la suivante :
I am Nokia, I can call you! I am iPhone, I can call you!
Le deuxième exemple d'interface :
Exemple
import "fmt"
type Shape interface {
area() float64
}
type Rectangle struct {
width float64
height float64
}
func (r Rectangle) area() float64 {
return r.width * r.height
}
type Circle struct {
radius float64
}
func (c Circle) area() float64 {
return 3.14 * c.radius * c.radius
}
func main() {
var s Shape
s = Rectangle{width: 10, height: 5}
fmt.Printf("Aire du rectangle : %f\n", s.area())
s = Circle{radius: 3}
fmt.Printf("Aire du cercle : %f\n", s.area())
}
Dans l'exemple ci-dessus, nous avons défini une interface Shape, qui définit une méthode area() qui retourne une valeur d'aire de type float64. Ensuite, nous avons défini deux structures, Rectangle et Circle, qui implémentent respectivement la méthode area() de l'interface Shape. Dans la fonction main(), nous avons d'abord défini une variable s de type Shape, puis nous lui avons respectivement assigné des instances de Rectangle et Circle, et via la méthode area() nous avons calculé leurs aires et les avons affichées. La sortie est la suivante :
矩形面积: 50.000000 圆形面积: 28.260000
Il convient de noter qu'une variable de type interface peut stocker une valeur de tout type qui implémente l'interface. Dans l'exemple, nous avons assigné des instances des deux types Rectangle et Circle à la variable s de type Shape, et appelé leurs méthodes de calcul d'aire via la méthode area().
autres extensions