web-dev-qa-db-fra.com

Implémentation d'interface anonyme à Golang

Dans Go, existe-t-il un moyen de satisfaire une interface anonymement? Cela ne semble pas être le cas, mais c'était ma meilleure tentative.

(Dans le Terrain de jeu )

package main

import "fmt"

type Thing interface {
    Item() float64
    SetItem(float64)
}

func newThing() Thing {
    item := 0.0
    return struct {
        Item (func() float64)
        SetItem (func(float64))
    }{
        Item: func() float64 { return item },
        SetItem: func(x float64) { item = x },
    }
}

func main() {
    thing := newThing()
    fmt.Println("Hello, playground")
    fmt.Println(thing)
}
31
jocull

Allez utilise method sets pour déclarer les méthodes appartenant à un type. Il n'y a qu'un seul moyen de déclarer des fonctions avec des types de récepteurs (méthodes):

func (v T) methodName(...) ... { }

Les fonctions imbriquées étant interdites, il est impossible de définir un ensemble de méthodes sur des structures anonymes.

La deuxième chose qui ne le permet pas, c'est que les méthodes sont en lecture seule. Les valeurs de méthode ont été introduites pour permettre de passer des méthodes et de les utiliser dans des goroutines, mais pas pour manipuler le jeu de méthodes.

Ce que vous pouvez faire à la place est de fournir un ProtoThing et de vous référer aux implémentations sous-jacentes de votre structure anonyme ( on play ):

type ProtoThing struct { 
    itemMethod func() float64
    setItemMethod func(float64)
}

func (t ProtoThing) Item() float64 { return t.itemMethod() }
func (t ProtoThing) SetItem(x float64) { t.setItemMethod(x) }

// ...

t := struct { ProtoThing }{}

t.itemMethod = func() float64 { return 2.0 }
t.setItemMethod = func(x float64) { item = x }

Cela fonctionne car en incorporant ProtoThing, le jeu de méthodes est hérité. Ainsi, la structure anonyme satisfait également l'interface Thing.

38
nemo

Voici un moyen astucieux de satisfaire une interface avec une fonction anonyme.

type Thinger interface {
    DoThing()
}

type DoThingWith func()

// Satisfy Thinger interface.
// So we can now pass an anonymous function using DoThingWith, 
// which implements Thinger.
func (thing DoThingWith) DoThing() {
    // delegate to the anonymous function
    thing()
}

type App struct {
}

func (a App) DoThing(f Thinger) {
    f.DoThing()
}


//...Somewhere else in your code:
app := App{}

// Here we use an anonymous function which satisfies the interface
// The trick here is to convert the anonymous function to the DoThingWith type
// which delegates to the anonymous function

app.DoThing(DoThingWith(func() {
    fmt.Println("Hey interface, are you satisfied?")
}))

Aire de jeux: https://play.golang.org/p/k8_X9g2NYc

nb, il semble que HandlerFunc dans le paquet http utilise ce modèle: https://golang.org/pkg/net/http/#HandlerFunc

edit: Modifiez le type DoThing en DoThingWith pour plus de clarté. Terrain de jeu mis à jour

8
Allen Hamilton

Vous ne pouvez pas instancier une structure avec des méthodes, elles doivent être déclarées en tant que fonctions, mais dans Go, les fonctions sont des "citoyens de première classe"; elles peuvent donc être des valeurs de champ, comme en JavaScript (mais typées).

Vous pouvez créer une structure générique qui accepte les champs func pour implémenter l’interface:

package main

import "fmt"

type Thing interface {
    Item() float64
    SetItem(float64)
}

// Implements Thing interface
type thingImpl struct {
    item    func() float64
    setItem func(float64)
}
func (i thingImpl) Item() float64     { return i.item() }
func (i thingImpl) SetItem(v float64) { i.setItem(v) }

func newThing() Thing {
    item := 0.0
    return thingImpl{
        item:    func() float64 { return item },
        setItem: func(x float64) { item = x },
    }
}

func main() {
    thing := newThing()
    fmt.Println("Hello, playground")
    fmt.Println(thing)
}
0
tothemario