it-swarm.com.de

ist es möglich, überschriebene Methoden von der übergeordneten Struktur in Golang aufzurufen?

Ich möchte einen solchen Code implementieren, bei dem B von A erben und nur die Foo () - Methode von A überschreiben, und ich hoffe, dass der Code B.Foo () druckt, aber es weiterhin A.Foo () druckt, scheint der Empfänger Golang kann in C++ nicht so arbeiten. Wenn die dynamische Bindung aktiviert ist, kann der Code wie ich arbeiten. 

Ich poste auch einen anderen Code, der funktioniert, aber es ist zu schwer zu implementieren, und eher wie ein Hack, denke ich, dass es kein Golang-Stil ist.

Mein Problem ist also: Wenn die Bar () - Methode des Elternteils über eine bestimmte Logik verfügt, öffnen Sie beispielsweise eine Datei, lesen Sie dann einige Zeilen und verwenden Sie Foo (), um diese Zeilen an stdout auszugeben, und das Child (im Beispiel ist B). möchte die meisten von ihnen verwenden, der einzige Unterschied ist, dass das Kind möchte, dass Foo () die Zeilen in eine andere Datei ausgibt. Wie soll ich es umsetzen? Ich habe gehört, dass Golangs Vererbung nicht wie C++ oder Java funktionieren kann und was ist der richtige Weg in Golang?

package main 

import ( 
        "fmt" 
) 

type A struct { 
} 

func (a *A) Foo() { 
        fmt.Println("A.Foo()") 
} 

func (a *A) Bar() { 
        a.Foo() 
} 

type B struct { 
        A 
} 

func (b *B) Foo() { 
        fmt.Println("B.Foo()") 
} 

func main() { 
        b := B{A: A{}} 
        b.Bar() 
}

output: A.Foo()

das folgende Stück funktioniert aber beim schreiben 

a := A{}
a.Bar()

sie werden einen Compiler-Fehler erhalten

package main

import (
    "fmt"
)

type I interface {
    Foo()
}

type A struct {
    i I

}

func (a *A) Foo() {
    fmt.Println("A.Foo()")
}

func (a *A) Bar() {
    a.i.Foo()

}

type B struct {
    A
}

func (b *B) Foo() {
    fmt.Println("B.Foo()")
}

func main() {
    b := B{A: A{}}
    b.i = &b     // here i works like an attribute of b
    b.Bar()

output: B.Foo()
42
zhaozhi

Wie Sie geschrieben haben, ist Go nicht wirklich Vererbung. Die Methode, die Vererbung wie Features zulässt, heißt Einbetten. 

http://golang.org/doc/effective_go.html#embedding

Im Wesentlichen bedeutet dies, dass die eingebettete Struktur keine Ahnung hat, dass sie eingebettet ist. Sie können also nichts außer Kraft setzen, was von ihr aufgerufen wird. Sie können die eingebettete Struktur tatsächlich nehmen und nur von der Einbettungsstruktur eine Referenz dafür nehmen.

Ihr bester Weg, dies zu tun, ist also mehr oder weniger wie Ihr zweites Beispiel - durch eine Art Abhängigkeitsinjektion unter Verwendung von Schnittstellen. Das heißt - A hat einen Verweis auf eine Schnittstelle, die die eigentliche Arbeit ausführt, z. B. worker, die in eine Datei oder was auch immer schreibt. Wenn Sie dann B instanziieren, ersetzen Sie auch A's worker durch einen anderen Worker (Sie können dies natürlich auch tun, ohne A einzubetten). Das A macht so etwas wie myWorker.Work(), ohne sich um den Arbeiter zu kümmern.

16
Not_a_Golfer
package main

import (
    "fmt"
)


//-- polymorphism in work

// children specification by methods signatures
// you should define overridable methods here
type AChildInterface interface {
    Foo()
}

type A struct {
    child AChildInterface
}

//-- /polymorphism in work


// hard A.Bar method
func (a *A) Bar() {
    a.child.Foo() // Foo() will be overwritten = implemented in a specified child
}


//-- default implementations of changeable methods

type ADefaults struct{}

func (ad ADefaults) Foo() {
    fmt.Println("A.Foo()")
}

//-- /default


//-- specified child

type B struct {
    ADefaults // implement default A methods from ADefaults, not necessary in this example
}

// overwrite specified method
func (b B) Foo() {
    fmt.Println("B.Foo()")
}

//-- /specified child

func main() {
    a := A{ADefaults{}}
    a.Bar()

    // Golang-style inheritance = embedding child
    b := A{B{}} // note: we created __Parent__ with specified __Child__ to change behavior
    b.Bar()
}

Ausgabe:

A.Foo()
B.Foo()
3
nordborn

Vor kurzem habe ich ein Bedürfnis, dies zu tun, und die von OP vorgeschlagene Kompositionsmethode funktioniert hervorragend.

Ich versuche, ein weiteres Beispiel zu erstellen, um die Beziehung zwischen Eltern und Kindern zu demonstrieren und sie lesbarer zu machen.

https://play.golang.org/p/9EmWhpyjHf :

package main

import (
    "fmt"
    "log"
)

type FruitType interface {
    Wash() FruitType
    Eat() string
}

type Fruit struct {
    name  string
    dirty bool
    fruit FruitType
}

func (f *Fruit) Wash() FruitType {
    f.dirty = false
    if f.fruit != nil {
        return f.fruit
    }
    return f
}
func (f *Fruit) Eat() string {
    if f.dirty {
        return fmt.Sprintf("The %s is dirty, wash it first!", f.name)
    }
    return fmt.Sprintf("%s is so delicious!", f.name)
}

type Orange struct {
    *Fruit
}

func NewOrange() *Orange {
    ft := &Orange{&Fruit{"Orange", true, nil}}
    ft.fruit = ft
    return ft
}
func NewApple() *Fruit {
    ft := &Fruit{"Apple", true, nil}
    return ft
}

func (o *Orange) Eat() string {
    return "The orange is so sour!"
}

func main() {
    log.Println(NewApple().Eat())
    log.Println(NewApple().Wash().Eat())
    log.Println(NewOrange().Eat())
    log.Println(NewOrange().Wash().Eat())
}
2
Nothize T

Go unterstützt das Überschreiben von virtuellen Methoden nicht. Das gewünschte Designmuster wird von Go also nicht direkt unterstützt. Dies wird als schlechte Praxis betrachtet, da durch das Ändern der Implementierung von A.Bar () alle abgeleiteten Klassen wie B zerstört werden, die davon ausgehen, dass A.Foo () von A.Bar () aufgerufen wird. Das gewünschte Designmuster macht den Code spröde.

Die Vorgehensweise in Go wäre das Definieren einer Fooer-Schnittstelle mit einer Foo () - Methode. Ein Fooer wird als Argument an Bar () übergeben oder in einem Feld von A gespeichert und von A.Bar () aufgerufen. Um die Foo-Aktion zu ändern, ändern Sie den Fooer-Wert. Dies wird Komposition genannt und ist viel besser als das Ändern der Foo-Aktion durch Vererbung und Überschreiben von Methoden. 

Hier finden Sie eine idiomatische Methode, um das zu tun, was Sie in Go tun möchten: https://play.golang.org/p/jJqXqmNUEHn . In dieser Implementierung ist der Fooer ein Mitgliedsfeld von A, das durch einen Parameter der Instanz-Factory-Funktion NewA() initialisiert wird. Dieses Entwurfsmuster ist vorzuziehen, wenn sich der Fooer während der Lebensdauer von A nicht häufig ändert. Andernfalls können Sie den Fooer als Parameter der Bar()-Methode übergeben. 

So ändern wir das Verhalten von Foo() in Go. Sie wird Komposition genannt, weil Sie das Verhalten von Bar() ändern, indem Sie die Instanzen ändern, aus denen A erstellt wird.

package main

import (
    "fmt"
)

type Fooer interface {
    Foo()
}

type A struct {
    f Fooer
}

func (a *A) Bar() {
    a.f.Foo()
}

func NewA(f Fooer) *A {
    return &A{f: f}
}

type B struct {
}

func (b *B) Foo() {
    fmt.Println("B.Foo()")
}

type C struct {
}

func (c *C) Foo() {
    fmt.Println("C.Foo()")
}

func main() {
    a := NewA(new(B))
    a.Bar()

    a.f = &C{}
    a.Bar()
}

PS: Hier ist eine mögliche Implementierung des Entwurfsmusters, das Sie zu Dokumentationszwecken implementieren wollten: https://play.golang.org/p/HugjIbYbout

1
chmike

Ich habe mit mir selbst zu kämpfen. 2 Lösungen gefunden:

  1. Idiomatic Go way : Implementiere die allgemeine Methode, die die "virtuelle" Methode als externe Funktion mit Schnittstelle als Argument aufruft.

    package main
    
    import "fmt"
    
    type ABCD interface {
        Foo()
    }
    
    type A struct {
    }
    
    func (a *A) Foo() {
        fmt.Println("A.Foo()")
    }
    
    type B struct {
        A
    }
    
    func (b *B) Foo() {
        fmt.Println("B.Foo()")
    }
    
    // Bar is common "method", as external function.
    func Bar(a ABCD) {
        a.Foo()
    }
    
    func main() {
        b := &B{} // note it is a pointer
        // also there's no need to specify values for default-initialized fields.
        Bar(b) // prints: B.Foo()
    }
    
  2. Ähnlich wie bei Ihrer zweiten Option: Interface Hackery . Da Bar () jedoch nicht spezifisch für A ist (ist für A und B üblich), verschieben wir es in die Basisklasse und blenden Implementierungsdetails und alle gefährlichen Dinge aus:

    package main
    
    import "fmt"
    
    //////////////////////////////////////////////////////////////////////
    // Implementation.
    
    // aBase is common "ancestor" for A and B.
    type aBase struct {
        ABCD // embed the interface. As it is just a pointer, it has to be initialized!
    }
    
    // Bar is common to A and B.
    func (a *aBase) Bar() {
        a.Foo() // aBase has no method Foo defined, so it calls Foo method of embedded interface.
    }
    
    // a class, not exported
    type a struct {
        aBase
    }
    
    func (a *a) Foo() {
        fmt.Println("A.Foo()")
    }
    
    // b class, not exported
    type b struct {
        aBase
    }
    
    func (b *b) Foo() {
        fmt.Println("B.Foo()")
    }
    
    //////////////////////////////////////////////////////////////////////
    // Now, public functions and methods.
    
    // ABCD describes all exported methods of A and B.
    type ABCD interface {
        Foo()
        Bar()
    }
    
    // NewA returns new struct a
    func NewA() ABCD {
        a := &a{}
        a.ABCD = a
        return a
    }
    
    // NewB returns new struct b
    func NewB() ABCD {
        b := &b{}
        b.ABCD = b
        return b
    }
    
    func main() {
        b := NewB()
        b.Bar() // prints: B.Foo()
    
        a := NewA()
        a.Bar() // prints: A.Foo()
    }
    
0
metalim