¿es posible llamar al método reemplazado desde la estructura padre en golang?

Quiero implementar dicho código, donde B hereda de A y solo reemplaza el método Foo () de A, y espero que el código imprima B.Foo (), pero aún así imprima A.Foo (), parece que el receptor en Golang no puede funcionar así en C ++, en el que cuando el enlace dynamic está habilitado, el código puede funcionar como yo quiero.

También publico otra pieza de código, que funciona, pero es muy difícil de implementar, y más como un truco, creo que no es un estilo Golang.

Entonces mi problema es: si el método Bar () de los padres tiene algo de lógica, por ejemplo, abra un archivo, luego lea algunas líneas, y use Foo () para dar salida a estas líneas a stdout, y el Niño (en el ejemplo es B) quiere usar la mayoría de ellos, la única diferencia es que el Niño quiere que Foo () emita las líneas a otro archivo. ¿Cómo debería implementarlo? He oído que la herencia de Golang no puede funcionar como C ++ o Java, y ¿cuál es el camino correcto en 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() 

la siguiente pieza funciona, pero cuando se escribe

 a := A{} a.Bar() 

encontrará un error de comstackción

 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() { aiFoo() } type B struct { A } func (b *B) Foo() { fmt.Println("B.Foo()") } func main() { b := B{A: A{}} bi = &b // here i works like an attribute of b b.Bar() output: B.Foo() 

Como usted escribió, lo que Go tiene no es realmente herencia, el método que permite características similares a la herencia se llama Incrustación.

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

Lo que significa básicamente es que la estructura incrustada no tiene idea de que está incrustada, por lo que no puede anular nada que se invoque desde ella. En realidad, puede tomar la estructura incrustada y tomar una referencia solo desde la estructura incrustada.

Entonces, su mejor forma de hacerlo es más o menos como su segundo ejemplo: mediante algún tipo de dependency injection utilizando interfaces. es decir, A tiene una referencia a alguna interfaz que hace el trabajo real, por ejemplo worker , que escribe en un archivo o lo que sea. Luego, al crear una instancia de B, también reemplaza al worker con otro trabajador (puede hacerlo incluso sin incrustar A, por supuesto). El A solo hace algo como myWorker.Work() sin importar qué trabajador sea.

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

Salida:

 A.Foo() B.Foo() 

Recientemente, tengo la necesidad de hacer esto y el método de composición propuesto por OP funciona de maravilla.

Intento crear otro ejemplo para intentar demostrar la relación entre padres e hijos y facilitar la lectura.

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

Go no admite la anulación de método virtual. Por lo tanto, Go no admite directamente el patrón de diseño que desea utilizar. Se considera una mala práctica porque cambiar la implementación de A.Bar () rompería todas las clases derivadas, como B, que asume que A.Bar () llamará a A.Foo (). El patrón de diseño que desea utilizar hará que su código sea frágil.

La forma de hacerlo en Go sería definir una interfaz Fooer con un método Foo (). Un Fooer se pasará como argumento a Bar () o se almacenará en un campo de A y lo llamará A.Bar (). Para cambiar la acción de Foo, cambie el valor de Fooer. Esto se llama composición, y es mucho mejor que cambiar la acción de Foo por herencia y anulación de método.

Esta es una forma idiomática de hacer lo que desea hacer en Go: https://play.golang.org/p/jJqXqmNUEHn . En esta implementación, Fooer es un campo miembro de A que se inicializa mediante un parámetro de la función de fábrica de instancias NewA() . Este patrón de diseño es preferible cuando el Fooer no cambia con frecuencia durante la vida útil de A. De lo contrario, puede pasar el Fooer como parámetro del método Bar() .

Así es como cambiamos el comportamiento de Foo() en Go. Se llama composición porque cambia el comportamiento de Bar() al cambiar las instancias que componen A.

 package main import ( "fmt" ) type Fooer interface { Foo() } type A struct { f Fooer } func (a *A) Bar() { afFoo() } 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() af = &C{} a.Bar() } 

PD: Aquí hay una posible implementación del patrón de diseño que quería implementar para la documentación: https://play.golang.org/p/HugjIbYbout