2015-07-11 4 views
23

В Go есть ли способ анонимного интерфейса? Кажется, что нет, но это была моя лучшая попытка.Анонимная реализация интерфейса в Golang

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

ответ

27

Go использует method sets, чтобы объявить, какие методы принадлежат к типу. Существует только один способ объявить функции с ресивером типов (методы):

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

Поскольку вложенные функции запрещены, нет никакого способа определить метод набора анонимных структур.

Вторая вещь, которая не позволит этому, заключается в том, что методы доступны только для чтения. Method values, чтобы позволить проходить методы и использовать их в goroutines, но не манипулировать набором методов.

Что вы можете сделать вместо этого, чтобы обеспечить ProtoThing и относится к нижележащей реализации Вашей анонимной структуры (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 } 

Это работает потому, что встраивая ProtoThing наследуются набор метода. Таким образом, анонимная структура также удовлетворяет интерфейсу Thing.

+1

Это действительно потрясающе, и мне нравится, как это Полуструктурированное. Встраивание действительно опрятно. – jocull

+0

Сначала я прочитал часть «невозможно», но потом вернулся и фактически запустил ее! Хороший! –

8

Вот аккуратный способ удовлетворить интерфейс с анонимной функцией.

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

площадка: https://play.golang.org/p/k8_X9g2NYc

Н.Б., это выглядит как HandlerFunc в пакете HTTP использует этот шаблон: https://golang.org/pkg/net/http/#HandlerFunc

редактирования: Изменен тип DoThing для DoThingWith для ясности. Обновленная игровая площадка

+0

Эй, это действительно здорово! Хорошая работа! – jocull

0

Вы не можете создавать структуру с помощью методов, их нужно объявлять как функции, но в функциях Go есть «граждане первого класса», поэтому они могут быть значениями полей, как в JavaScript (но набраны).

Вы можете сделать родовую структуру, которая принимает Func поле для реализации интерфейса:

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) 
} 
Смежные вопросы