Ho familiarità con il fatto che, in Go, le interfacce definiscono la funzionalità piuttosto che i dati. Hai messo un insieme di metodi in un'interfaccia, ma non sei in grado di specificare alcun campo che sarebbe richiesto su tutto ciò che implementa quell'interfaccia.Vai campi di interfaccia
Ad esempio:
// Interface
type Giver interface {
Give() int64
}
// One implementation
type FiveGiver struct {}
func (fg *FiveGiver) Give() int64 {
return 5
}
// Another implementation
type VarGiver struct {
number int64
}
func (vg *VarGiver) Give() int64 {
return vg.number
}
Ora possiamo utilizzare l'interfaccia e le sue implementazioni:
// A function that uses the interface
func GetSomething(aGiver Giver) {
fmt.Println("The Giver gives: ", aGiver.Give())
}
// Bring it all together
func main() {
fg := &FiveGiver{}
vg := &VarGiver{3}
GetSomething(fg)
GetSomething(vg)
}
/*
Resulting output:
5
3
*/
Ora, quello che non puoi fare è qualcosa di simile:
type Person interface {
Name string
Age int64
}
type Bob struct implements Person { // Not Go syntax!
...
}
func PrintName(aPerson Person) {
fmt.Println("Person's name is: ", aPerson.Name)
}
func main() {
b := &Bob{"Bob", 23}
PrintName(b)
}
Tuttavia, dopo aver giocato con le interfacce e incorporato le strutture, ho scoperto un modo per fare questo, in un certo modo:
type PersonProvider interface {
GetPerson() *Person
}
type Person struct {
Name string
Age int64
}
func (p *Person) GetPerson() *Person {
return p
}
type Bob struct {
FavoriteNumber int64
Person
}
A causa della struct incorporato, Bob ha tutto persona ha. Implementa anche l'interfaccia PersonProvider, in modo tale che possiamo trasferire Bob in funzioni progettate per l'utilizzo di tale interfaccia.
func DoBirthday(pp PersonProvider) {
pers := pp.GetPerson()
pers.Age += 1
}
func SayHi(pp PersonProvider) {
fmt.Printf("Hello, %v!\r", pp.GetPerson().Name)
}
func main() {
b := &Bob{
5,
Person{"Bob", 23},
}
DoBirthday(b)
SayHi(b)
fmt.Printf("You're %v years old now!", b.Age)
}
Here is a Go Playground che dimostra il codice precedente.
Utilizzando questo metodo, è possibile creare un'interfaccia che definisce i dati piuttosto che il comportamento e che può essere implementata da qualsiasi struttura semplicemente incorporando tali dati. È possibile definire funzioni che interagiscono esplicitamente con i dati incorporati e non sono a conoscenza della natura della struttura esterna. E tutto è controllato al momento della compilazione! (L'unico modo per rovinare, che posso vedere, sarebbe incorporare l'interfaccia PersonProvider
in Bob
, piuttosto che una concreta Person
Sarebbe compilare e non riescono a runtime..)
Ora, ecco la mia domanda: è questo un trucco pulito, o dovrei farlo in modo diverso?
"Posso creare un'interfaccia che definisce i dati anziché il comportamento". Direi che hai un comportamento che restituisce i dati. – jmaloney
Scriverò una risposta; Penso che vada bene se ne hai bisogno e ne conosci le conseguenze, ma ci sono delle conseguenze e non lo farei sempre. – twotwotwo
@jmaloney Penso che tu abbia ragione, se volessi guardarlo chiaramente. Ma nel complesso, con i diversi pezzi che ho mostrato, la semantica diventa "questa funzione accetta qualsiasi struttura che abbia un ___ nella sua composizione". Almeno, è quello che intendevo. –