2015-07-19 13 views
7

Dire che ho due struct:Come possono due tipi diversi implementare lo stesso metodo in golang usando le interfacce?

type First struct { 
    str string 
} 
type Second struct { 
    str string 
} 

E voglio che ciascuno di essi per implementare l'interfaccia A:

type A interface { 
    PrintStr() //print First.str or Second.str 
} 

sembra ridondante avere un'implementazione per entrambe le prima e la seconda le strutture in questo modo:

func (f First) PrintStr() { 
    fmt.Print(f.str) 
} 

func (s Second) PrintStr() { 
    fmt.Print(s.str) 
} 

C'è un modo per disporre di un'implementazione per tutte le strutture che implementano l'interfaccia A? Qualcosa del genere, ma non sembra funzionare:

func (a A) PrintStr() { 
    fmt.Print(a.str) 
} 

Grazie!

+2

Sembra anche un po 'ridondante avere 2 strutture con gli stessi tipi. – TheHippo

+0

Sì, ma questo è un esempio di giocattolo. Primo e Secondo potrebbero condividere alcuni campi e non altri. Il punto è che voglio che una funzione si comporti esattamente nello stesso modo per due tipi diversi, senza dover ripetere il codice. – Ekaterina

risposta

10

No non si può, ma è possibile creare un tipo di base e poi integrarlo in un 2 struct, quindi solo bisogno di un'implementazione per il tipo base:

type WithString struct { 
    str string 
} 

type First struct { 
    WithString 
} 

type Second struct { 
    WithString 
} 

type A interface { 
    PrintStr() //print First.str or Second.str 
} 

func (w WithString) PrintStr() { 
    fmt.Print(w.str) 
} 

Usage:

a := First{ 
    WithString: WithString{ 
     str: "foo", 
    }, 
} 

Complete Example on Playground

Embed documentation

+1

cos'è questa stregoneria in cui definisci una struttura con un membro non variabile ?! dalla tua risposta: 'digita First struct {WithString}' ... c'è qualche documentazione su questo? definire una struct di tipo come un altro tipo? – Joey

+1

@Joey questo si chiama embedding ed è una tecnica ben nota, vedi https://golang.org/doc/effective_go.html#embedding – SirDarius

1
non

Forse il modo migliore per risolvere il problema, ma è possibile utilizzare un wrapper al fine di evitare "di esecuzione" la funzione due volte, qualcosa di simile:

type First struct { 
    str StringWrapper 
} 
type Second struct { 
    str StringWrapper 
} 


type StringWrapper struct { 
    str string 
} 
func (f StringWrapper) PrintStr() { 
    fmt.Print(f.str) 
} 

func main() { 
    var a First = First{str:StringWrapper{str: "aaa"}}; 
    a.str.PrintStr(); 
} 
2

Se la logica di stampa dipende dall'interfaccia ma non sulle struct stessi, allora è meglio spostare stampa a una funzione libera che opera su un'interfaccia.

Nel tuo caso, il metodo PrintStr viene utilizzato per stampare una stringa che è un membro di ogni struttura.
In questo caso, significa che ogni struttura deve implementare un'interfaccia che restituisce la stringa necessaria utilizzata per la stampa e PrintStr diventa una funzione che utilizza un parametro Printable.

type First struct { 
    str string 
} 
type Second struct { 
    str string 
} 

type Printable interface { 
    String() string 
} 

func (p First) String() string { 
    return p.str 
} 

func (p Second) String() string { 
    return p.str 
} 

func PrintStr(p Printable) { 
    fmt.Print(p.String()) 
} 

L'utilizzo dell'interfaccia A è non-idiomatica perché un interfaccia non dovrebbe dipendere dalla realizzazione della sua funzionalità.

Invece, con questa soluzione, è possibile mantenere l'interfaccia A, ma semplificare ogni implementazione:

func (f First) PrintStr() { 
    PrintStr(f) 
} 

func (s Second) PrintStr() { 
    PrintStr(s) 
} 

È ancora ridondante, ma la logica risiede nella funzione chiamata da lì, limitando la è necessario eseguire il copia-incolla in caso di modifica della logica di stampa.

Questo modello è comune nella libreria standard Go, poiché molte funzioni utili sono costruite su interfacce che non possono estendere, ad esempio io.Reader.
Si tratta di una semplice interfaccia con un solo metodo, ma viene utilizzata completamente da molti altri pacchetti.
Se si osserva la funzione ioutil.ReadAll, si potrebbe sostenere che avrebbe potuto essere implementato come un altro metodo dell'interfaccia io.Reader, tuttavia questo semplifica i lettori, concentrandosi sul loro singolo metodo, consentendo a qualsiasi implementatore di utilizzare ReadAll gratuitamente.

Problemi correlati