Type Composition: overriding interface types

旧城冷巷雨未停 提交于 2019-12-19 04:00:15

问题


I want to compose a type of another type, but replace one of the fields (which is an interface value) with a fake. The problem I am getting is the underlying field is being used, so I can't seem to override the field.

I've demoed the problem here: https://play.golang.org/p/lHGnyjzIS-Y

package main

import (
    "fmt"
)

type Printer interface {
    Print()
}

type PrinterService struct {}

func (ps PrinterService) Print() { fmt.Println("PrinterService") }

type Service struct {
    Client PrinterService
}

func (s Service) PrintViaMethod() { s.Client.Print() }

type FakeService struct {
    Service
    Client Printer
}

type SomeOtherService struct {}

func (sos SomeOtherService) Print() { fmt.Println("SomeOtherService") }

func main() {
    s := FakeService{Client: SomeOtherService{}}
    s.PrintViaMethod()
}

Why does it print "PrinterService"? I want it to print "SomeOtherService".

Thanks.


回答1:


By s.PrintViaMethod(), you are calling the promoted method FakeService.Service.PrintViaMethod(), and the method receiver will be FakeService.Service which is of type Service, and Service.PrintViaMethod() calls Service.Client.Print(), where Service.Client is of type which PrinterService, that's why it prints "PrinterService".

In Go there is embedding, but there is no polymorphism. When you embed a type in a struct, methods of the embedded type get promoted and will be part of the method set of the embedder type. But when such a promoted method is called, it will get the embedded value as the receiver, not the embedder.

To achieve what you want, you would have to "override" the PrintViaMethod() method by providing your implementation of it for the FakeService type (with FakeService receiver type), and inside it call FakeService.Client.Print().

By doing so s.PrintViaMethod() will denote the FakeService.PrintViaMethod() method as that will be at the shallowest depth where the PrintViaMethod() exists (and not FakeService.Service.PrintViaMethod()). This is detailed in Spec: Selectors.

For example:

func (fs FakeService) PrintViaMethod() {
    fs.Client.Print()
}

Then the output will be (try it on the Go Playground):

SomeOtherService

See related questions and answers with more details:

Go embedded struct call child method instead parent method

Does fragile base class issue exist in Go?




回答2:


Why does it print 'PrinterService'? I want it to print 'SomeOtherService'.

Because that's what your code says to do. PrintViaMethod calls s.Client.Print(), and s.Client is a (zero value) instance of PrinterService, which outputs PrinterService.

What you probably want is to call s.Print() in main(). I don't see any reason for your PrintByMethod function at all.




回答3:


As per Flimzy you are calling print s.Client.Print() which is of type PrinterService implemented as receiver to Print() function printing PrinterService. You can also change type of Client PrinterService in Service struct to Someother service

package embedded

import (
    "fmt"
)

type Printer interface {
    Print()
}

type PrinterService struct{}

func (ps PrinterService) Print() { fmt.Println("PrinterService") }

type Service struct {
    Client SomeOtherService
}

func (s Service) PrintViaMethod() { s.Client.Print() }

type FakeService struct {
    Service
    Client Printer
}

type SomeOtherService struct{}

func (sos SomeOtherService) Print() { fmt.Println("SomeOtherService") }

func Call() {
    s := FakeService{Client: SomeOtherService{}}
    s.PrintViaMethod()
}


来源:https://stackoverflow.com/questions/48079775/type-composition-overriding-interface-types

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!