2015-07-11 28 views
23

In Go, c'è un modo per soddisfare un'interfaccia anonimamente? Non sembra che ci sia, ma questo è stato il mio miglior tentativo.Implementazione di un'interfaccia anonima in Golang

(Nella Playground)

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) 
} 

risposta

27

Go utilizza method sets dichiarare quali metodi appartengono a un tipo. C'è solo un modo per dichiarare funzioni con tipi di ricevitore (metodi):

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

Poiché le funzioni nidificate sono proibiti, non c'è modo di definire un metodo impostato su struct anonimi.

La seconda cosa che non consente questo è che i metodi sono di sola lettura. Sono stati introdotti Method values per consentire il passaggio dei metodi e l'utilizzo in goroutine, ma non per manipolare il set di metodi.

Che cosa si può fare, invece è quello di fornire un ProtoThing e si riferiscono a implementazioni di fondo del vostro struct anonimo (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 } 

Questo funziona perché incorporando ProtoThing il set metodo viene ereditato. Pertanto la struttura anonima soddisfa anche l'interfaccia Thing.

+1

Questo è davvero impressionante, e mi piace come è semi-strutturata. L'incorporamento è davvero pulito. – jocull

+0

All'inizio ho letto la parte "non possibile", ma poi sono tornato e in realtà l'ho eseguito! Ben fatto! –

8

Ecco il modo pulito per soddisfare un'interfaccia con una funzione anonima.

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?") 
})) 

Playground: https://play.golang.org/p/k8_X9g2NYc

nb, sembra HandlerFunc nel pacchetto http utilizza questo schema: https://golang.org/pkg/net/http/#HandlerFunc

edit: Tipo Cambiato DoThing per DoThingWith per chiarezza. Campo di gioco aggiornato

+0

Ehi, è molto pulito! Bel lavoro! – jocull

0

Non è possibile creare un'istanza di una struttura con metodi, devono essere dichiarati come funzioni, ma nelle funzioni di Go sono "cittadini di prima classe", quindi possono essere valori di campo come in JavaScript (ma digitati).

È possibile effettuare una struct generica che accetta campi FUNC per implementare l'interfaccia:

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) 
}