2015-07-19 12 views
7

sagen, dass ich zwei structs haben:Wie können zwei verschiedene Typen die gleiche Methode in Golang mit Hilfe von Schnittstellen implementieren?

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

Und ich will sie beide Schnittstelle A implementieren:

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

Es überflüssig scheint, wie so eine Implementierung für beide Ersten und Zweiten structs zu haben:

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

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

Gibt es eine Möglichkeit, dass ich eine Implementierung für alle Strukturen haben kann, die Schnittstelle A implementieren? Etwas wie das, aber es scheint nicht zu funktionieren:

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

Vielen Dank!

+2

Es scheint auch ein wenig redundant, 2 Strukturen mit den gleichen Typen zu haben. – TheHippo

+0

Ja, aber das ist ein Spielzeugbeispiel. First und Second könnten einige Felder teilen und nicht andere. Der Punkt ist, dass ich möchte, dass sich eine Funktion auf genau dieselbe Weise für zwei verschiedene Typen verhält, ohne Code wiederholt zu haben. – Ekaterina

Antwort

10

Nein, Sie können nicht, aber Sie könnten eine Basistyp erstellen und einbinden, um sie dann in dem 2-Struktur, also nur eine Implementierung für den Basistyp benötigen:

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

Verbrauch:

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

Complete Example on Playground

Embed documentation

+1

Was ist diese Zauberei, wo Sie eine Struktur mit einem nicht-variablen Mitglied definieren ?! aus Ihrer Antwort: 'type First struct {WithString}' ... gibt es eine Dokumentation dazu? Definieren Sie eine Typstruktur als einen anderen Typ? – Joey

+1

@Joey Dies wird Einbettung genannt und ist eine bekannte Technik, siehe https://golang.org/doc/effective_go.html#embedding – SirDarius

1

Vielleicht nicht der beste Weg, um Ihr Problem zu lösen, aber man konnte einen Wrapper, um zu vermeiden, „Umsetzung“ die Funktion zweimal, so etwas wie folgt verwenden:

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

Wenn die Drucklogik auf der Schnittstelle hängt aber nicht auf dem structs selbst, dann ist es besser, den Druck auf eine freie Funktion zu bewegen, die über eine Schnittstelle arbeitet.

In Ihrem Fall wird die Methode PrintStr verwendet, um eine Zeichenfolge zu drucken, die ein Mitglied jeder Struktur ist.
In diesem Fall bedeutet dies, dass jede Struktur eine Schnittstelle implementieren muss, die die zum Drucken benötigte Zeichenfolge zurückgibt, und PrintStr wird zu einer Funktion, die einen Printable Parameter verwendet.

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

Ihre Nutzung des A Schnittstelle ist nicht-idiomatische weil eine Schnittstelle nicht auf der Implementierung seine Funktionalität abhängen.

Stattdessen mit dieser Lösung können Sie immer noch die A-Schnittstelle halten, sondern vereinfachen jede Implementierung:

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

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

Es ist noch redundant, aber die Logik liegt in der Funktion, die von dort aus aufgerufen wird, die Begrenzung muss im Fall einer Änderung der Drucklogik kopiert werden.

Dieses Muster ist in der Go-Standardbibliothek üblich, da viele nützliche Funktionen auf Schnittstellen basieren, die sie nicht erweitern können, z. B. io.Reader.
Es ist eine einfache Schnittstelle mit nur einer Methode, aber es wird gründlich von vielen anderen Paketen verwendet.
Wenn Sie die ioutil.ReadAll Funktion betrachten, könnte es argumentiert werden, dass es hätte als eine andere Methode der io.Reader Schnittstelle implementiert werden können, aber dies hält Leser einfacher, konzentriert sich auf ihre einzige Methode, während jeder Implementierer ReadAll kostenlos nutzen kann.

Verwandte Themen