web-dev-qa-db-fra.com

Pourquoi des interfaces sont-elles nécessaires dans Golang?

Dans Golang, nous utilisons des structures avec des méthodes de réception. tout est parfait jusqu'ici.
Cependant, je ne suis pas sûr de ce que sont les interfaces. Nous définissons les méthodes dans les structures et si nous voulons implémenter une méthode sur une structure, nous l'écrivons quand même sous une autre structure.
Cela signifie que les interfaces ne semblent être que des définitions de méthode, prenant juste un espace supplémentaire inutile sur notre page.

Existe-t-il un exemple expliquant pourquoi j'ai besoin d'une interface?

28
nikoss

Les interfaces sont un sujet trop vaste pour donner une réponse détaillée ici, mais certaines choses pour en rendre l'utilisation claire.

Les interfaces sont un outil . Que vous les utilisiez ou non, cela dépend de vous, mais ils peuvent rendre le code plus clair et ils peuvent fournir une belle API entre les packages ou les clients (utilisateurs) et les serveurs (fournisseurs).

Oui, vous pouvez créer votre propre type struct et vous pouvez y "attacher" des méthodes, par exemple:

type Cat struct{}

func (c Cat) Say() string { return "meow" }

type Dog struct{}

func (d Dog) Say() string { return "woof" }

func main() {
    c := Cat{}
    fmt.Println("Cat says:", c.Say())
    d := Dog{}
    fmt.Println("Dog says:", d.Say())
}

Nous pouvons déjà voir une certaine répétition dans le code ci-dessus: en faisant dire à la fois Cat et Dog. Pouvons-nous traiter les deux comme le même type d'entité, comme animal ? Pas vraiment. Bien sûr, nous pourrions gérer les deux en tant que interface{}, Mais si nous le faisons, nous ne pouvons pas appeler leur méthode Say() car une valeur de type interface{} Ne définit aucune méthode.

Il existe une certaine similitude dans les deux types ci-dessus: les deux ont une méthode Say() avec la même signature (paramètres et types de résultats) . Nous pouvons capturer ceci avec une interface:

type Sayer interface {
    Say() string
}

L'interface contient uniquement les signatures des méthodes, mais pas leur implémentation .

Notez que dans Go, un type implicite implémente une interface si son ensemble de méthodes est un sur-ensemble de l'interface. Il n'y a aucune déclaration d'intention. Qu'est-ce que ça veut dire? Nos précédents types Cat et Dog implémentent déjà cette interface Sayer même si cette définition d'interface n'existait même pas lorsque nous les avons écrites plus tôt, et nous ne les avons pas touchées pour les marquer ou quelque chose. Ils font juste.

Les interfaces spécifient le comportement . Un type qui implémente une interface signifie que ce type a toutes les méthodes que l'interface "prescrit".

Étant donné que les deux implémentent Sayer, nous pouvons gérer les deux comme une valeur de Sayer, ils ont ceci en commun. Voyez comment nous pouvons gérer les deux dans l'unité:

animals := []Sayer{c, d}
for _, a := range animals {
    fmt.Println(reflect.TypeOf(a).Name(), "says:", a.Say())
}

(Cette partie reflète uniquement pour obtenir le nom du type, n'en faites pas grand chose pour l'instant.)

La partie importante est que nous pourrions gérer à la fois Cat et Dog comme le même type (un type d'interface), et travailler avec eux/les utiliser. Si vous deviez rapidement créer des types supplémentaires avec une méthode Say(), ils pourraient s'aligner à côté de Cat et Dog:

type Horse struct{}

func (h Horse) Say() string { return "neigh" }

animals = append(animals, Horse{})
for _, a := range animals {
    fmt.Println(reflect.TypeOf(a).Name(), "says:", a.Say())
}

Supposons que vous souhaitiez écrire un autre code compatible avec ces types. Une fonction d'aide:

func MakeCatTalk(c Cat) {
    fmt.Println("Cat says:", c.Say())
}

Oui, la fonction ci-dessus fonctionne avec Cat et avec rien d'autre. Si vous souhaitez quelque chose de similaire, vous devez l'écrire pour chaque type. Inutile de dire à quel point c'est grave.

Oui, vous pouvez l'écrire pour prendre un argument de interface{} Et utiliser assertion de type ou commutateurs de type , ce qui réduirait le nombre de fonctions d'assistance, mais semble toujours très moche.

La solution? Oui, les interfaces. Déclarez simplement la fonction pour prendre une valeur d'un type d'interface qui définit le comportement que vous voulez en faire, et c'est tout:

func MakeTalk(s Sayer) {
    fmt.Println(reflect.TypeOf(s).Name(), "says:", s.Say())
}

Vous pouvez appeler cette fonction avec une valeur de Cat, Dog, Horse ou tout autre type inconnu jusqu'à présent, qui a une méthode Say() . Cool.

Essayez ces exemples sur le Go Playground .

64
icza

l'interface fournit certains types de génériques. Pensez à taper du canard.

type Reader interface{
     Read()
}

func callRead(r Reader){
      r.Read()
}

type A struct{
}
func(_ A)Read(){
}

type B struct{
}
func(_ B)Read(){
}

Il est correct de passer struct A et B à callRead, car les deux implémentent l'interface Reader. Mais si sans interface, nous devons écrire deux fonctions pour A et B.

func callRead(a A){
     a.Read()
}

func callRead2(b B){
     b.Read()
}
5
zzn

Je vais montrer ici, deux cas d'utilisation intéressants des interfaces dans Go:

1- Voir ces deux interfaces simples:

type Reader interface {
    Read(p []byte) (n int, err error)
}

type Writer interface {
    Write(p []byte) (n int, err error)
}

En utilisant ces deux interfaces simples, vous pouvez faire cette magie intéressante:

package main

import (
    "bufio"
    "bytes"
    "fmt"
    "io"
    "os"
    "strings"
)

func main() {
    file, err := os.Create("log.txt")
    if err != nil {
        panic(err)
    }
    defer file.Close()

    w := io.MultiWriter(file, os.Stdout)
    r := strings.NewReader("You'll see this string twice!!\n")
    io.Copy(w, r)

    slice := []byte{33, 34, 35, 36, 37, 38, 39, 10, 13}
    io.Copy(w, bytes.NewReader(slice)) // !"#$%&'

    buf := &bytes.Buffer{}
    io.Copy(buf, bytes.NewReader(slice))
    fmt.Println(buf.Bytes()) // [33 34 35 36 37 38 39 10 13]

    _, err = file.Seek(0, 0)
    if err != nil {
        panic(err)
    }

    r = strings.NewReader("Hello\nWorld\nThis\nis\nVery\nnice\nInterfacing.\n")
    rdr := io.MultiReader(r, file)
    scanner := bufio.NewScanner(rdr)
    for scanner.Scan() {
        fmt.Println(scanner.Text())
    }
}

Production:

You'll see this string twice!!
!"#$%&'

[33 34 35 36 37 38 39 10 13]
Hello
World
This
is
Very
Nice
Interfacing.
You'll see this string twice!!
!"#$%&'

J'espère que ce code est assez clair:
lit à partir de la chaîne en utilisant strings.NewReader et écrit simultanément dans file et os.Stdout en utilisant io.MultiWriter avec juste io.Copy(w, r). Lit ensuite à partir de la tranche à l'aide de bytes.NewReader(slice) et écrit simultanément à la fois file et os.Stdout. Copiez ensuite la tranche dans le tampon io.Copy(buf, bytes.NewReader(slice)) puis allez dans le fichier Origin en utilisant file.Seek(0, 0) puis lisez d'abord à partir de la chaîne en utilisant strings.NewReader Puis continuez à lire ce file en utilisant io.MultiReader(r, file) et bufio.NewScanner et Imprimez le tout en utilisant fmt.Println(scanner.Text()).


2- Et ceci est une autre utilisation intéressante de l'interface:

package main

import "fmt"

func main() {
    i := show()
    fmt.Println(i) // 0

    i = show(1, 2, "AB", 'c', 'd', []int{1, 2, 3}, [...]int{1, 2})
    fmt.Println(i) // 7

}
func show(a ...interface{}) (count int) {
    for _, b := range a {
        if v, ok := b.(int); ok {
            fmt.Println("int: ", v)
        }
    }
    return len(a)
}

production:

0
int:  1
int:  2
7

Et un bel exemple pour voir: expliquer les assertions de type dans Go

Voir aussi: Aller: Quelle est la signification de l'interface {}?

3
user6169399
  1. si vous avez besoin d'une méthode à implémenter quelle que soit la structure.

    vous pourriez avoir une méthode de gestionnaire pour accéder à vos structures locales et utiliser le gestionnaire avant de connaître la structure.

  2. si vous avez besoin d'un comportement unique à une autre structure ou à une structure courante.

    vous souhaiterez peut-être que votre interface soit affichée avec peu de méthodes, car les utilisateurs pourraient ne jamais les utiliser. vous voudrez peut-être que vos structures soient divisées par ses cas d'utilisation.

  3. si vous avez besoin d'un type qui implémente n'importe quoi.

    vous savez peut-être ou non le type mais au moins vous avez la valeur.

1
You sir