2015-08-24 20 views
13

Ich möchte eine Basisstruktur mit Methoden in meiner Bibliothek bereitstellen, die "erweitert" werden kann.Golang und Vererbung

Die Methoden dieser Basisstruktur basieren auf Methoden der Extending-Struktur. Dies ist in Go nicht direkt möglich, da Strukturmethoden nur Zugriff auf die eigenen Felder der Struktur haben, nicht auf Parent-Strukturen.

Der Punkt ist, Funktionalität zu haben, die ich nicht in jeder ausdehnenden Klasse wiederholen muss.

Ich habe dieses Muster gefunden, das gut funktioniert, , aber sieht aufgrund seiner zyklischen Struktur ziemlich verschlungen aus.

Ich habe noch nie so etwas in anderen Go-Code gefunden. Ist das sehr un-gehen? Welchen anderen Ansatz könnte ich nehmen?

type MyInterface interface { 
    SomeMethod(string) 
    OtherMethod(string) 
} 

type Base struct{ 
    B MyInterface 
} 

func (b *Base) SomeMethod(x string) { 
    b.B.OtherMethod(x) 
} 

type Extender struct { 
    Base 
} 

func (b *Extender) OtherMethod(x string) { 
    // Do something... 
} 

func NewExtender() *Extender { 
    e := Extender{} 
    e.Base.B = &e 
    return &e 
} 
+14

Es ist schwer zu sagen, was Sie von einem generischen Beispiel tun, aber das ist nicht idiomatischer Go-Code. Im Allgemeinen müssen Sie über Lösungen über Klassen und Vererbung insgesamt nachdenken. Verwenden Sie die Komposition zu Ihrem Vorteil, und denken Sie daran, dass nicht alles eine Struktur sein muss, die durch eine Schnittstelle repräsentiert wird - manchmal sind Funktionen alles, was Sie brauchen. – JimB

+11

Redesign Ihrer Lösung. Go hat keine Vererbung. Der Versuch, die Vererbung mit dem, was Go bietet, umzuwandeln, wird höchstwahrscheinlich fehlschlagen. – Volker

+10

Verstehe nicht, warum du abgelehnt wirst; Sie haben gefragt, ob dies ein geeigneter Ansatz ist, und wenn nicht, was Sie tun könnten, um es besser zu machen. Auf die Gefahr hin, wie der seltsame Typ auszusehen, denke ich, dass deine Frage in Ordnung ist. Nachdem das gesagt wurde, trafen die beiden Kommentatoren über mir genau richtig. –

Antwort

11

Wie in den Kommentaren erwähnt, ermutigt Go die Komposition über die Vererbung.

Um Ihre Frage zur Reduzierung der Code-Duplizierung zu beantworten, möchten Sie embedding verwenden.

das Beispiel aus Effective Go oben verlinkten verwenden, starten Sie mit sehr schmalen Schnittstellen, die nur ein paar Dinge tun:

type Reader interface { 
    Read(p []byte) (n int, err error) 
} 

type Writer interface { 
    Write(p []byte) (n int, err error) 
} 

Dann können Sie entweder Schnittstellen komponieren zusammen in einer anderen Schnittstelle:

// ReadWriter is the interface that combines the Reader and Writer interfaces. 
type ReadWriter interface { 
    Reader 
    Writer 
} 

Es funktioniert ähnlich für Strukturen, wo Sie Strukturen zusammenstellen können, die Reader und Writer in einer anderen Struktur zusammen implementieren:

type MyReader struct {} 
func (r *MyReader) Read(p []byte) (n int, err error) { 
    // Implements Reader interface. 
} 
type MyWriter struct {} 
func (w *MyWriter) Write(p []byte) (n int, err error) { 
    // Implements Writer interface. 
} 

// MyReadWriter stores pointers to a MyReader and a MyWriter. 
// It implements ReadWriter. 
type MyReadWriter struct { 
    *MyReader 
    *MyWriter 
} 

Grundsätzlich kann alles, was Reader oder Writer implementiert, wiederverwendet werden, indem sie in einer Struktur zusammengefügt werden, und diese äußere Struktur implementiert automatisch die Schnittstelle ReaderWriter.

Dies ist im Grunde tun Dependency Injection, und es ist auch sehr nützlich zum Testen.

Beispiel aus dem struct Code oben:

func (rw *MyReadWriter) DoCrazyStuff() { 
    data := []byte{} 
    // Do stuff... 
    rw.Read(data) 
    rw.Write(data) 
    // You get the idea... 
} 

func main() { 
    rw := &MyReadWriter{&MyReader{}, &MyWriter{}} 
    rw.DoCrazyStuff() 
} 

Eine Sache, darauf hinzuweisen, dass aus anderen Sprachen Zusammensetzung Paradigma etwas anders ist, ist, dass die MyReadWriter struct jetzt sowohl als Reader und Writer wirken kann. Deshalb geben wir in DoCrazyStuff()rw.Read(data) statt rw.Reader.Read(data).

UPDATE: Falsches Beispiel behoben.

+0

Außer Sie wollen fast nie einen Zeiger auf eine Schnittstelle verwenden. Ihr 'MyReadWriter' sollte ** nicht ** Zeiger auf Schnittstellen verwenden, sondern stattdessen 'type MyReadWriter struct {Reader, Writer}'. Dies ermöglicht (z. B.) rw: = MyReadWriter {strings.NewReader ("foo"), neu (bytes.Buffer)} '. –

+0

Danke für die Köpfe, das ist gut zu wissen. Haben Sie einen Link, der erklärt warum? Auch dieses Beispiel ist direkt von der effektiven Go-Seite. – Addison

+0

Ein guter Ort, um mehr über Schnittstellen zu erfahren, ist [ein Blog-Artikel von Russ Cox] (http://research.swtch.com/interfaces). –

5

Es tut uns leid, Sie zu enttäuschen, aber Sie stellen die falsche Frage. Ich hatte ein ähnliches Problem, als ich anfing, Go-Code zu schreiben.

Sie können nicht einfach eine Klassenhierarchie nehmen und sie in Go-Code übersetzen, zumindest nicht mit zufriedenstellenden Ergebnissen. Normalerweise gibt es einen sehr eleganten und einfachen Weg, um solche Dinge in Go zu lösen, aber um sie zu entdecken, müssen Sie ein wenig anders denken, als Sie es gewohnt sind.

Leider sagt Ihre Frage nichts über was Problem, das Sie versuchen zu lösen. Sie haben beschrieben, wie Sie es lösen möchten. Daher zögere ich, eine allgemeine Antwort zu geben, da dies nicht zu idiomatischem Go-Code führt. Ich verstehe, wenn Sie von dieser Antwort enttäuscht sind, aber meiner Meinung nach ist das die wertvollste Antwort, die Sie bekommen können :)