简体   繁体   中英

Go factory method returns type interface, not the struct which implements the interface

I am trying to create a factory method that returns a constructor for a struct that implements some interface.

Here is some sample code that illustrates the pattern I'm using.

// Generic Interface
type Foo interface {
    Bar() string
}

type FooConstructor func(name string) Foo

// A struct that implements Foo
type RealFoo struct {
    Name string
}

func (f *RealFoo) Bar() string {
    return f.Name
}

func NewRealFoo(name string) Foo {
    return &RealFoo{Name: name}
}

// Factory method to return some FooConstructor
func FooFactory() FooConstructor {
    // based on some logic, return some Foo constructor
    return NewRealFoo
}

func main() {
    ff := FooFactory()
    f := ff("baz")
    fmt.Println(f.Bar())
    fmt.Println(f.Name)
}

http://play.golang.org/p/0RzXlIGAs8

When I try and access a field on my struct that is not defined in the interface, I get an error:

f.Name undefined (type Foo has no field or method Name)

I believe the problem is that my constructor, func NewRealFoo(name string) Foo has the interface as the return type, instead of *RealFoo . But in order to use it as a type FooConstructor for the factory method, the return type must be the Foo interface.

How can I fix my code so that I can access the field f.Name ?

You are actually returning the RealFoo object, but as an implementation of Foo .

To get the field of the RealFoo struct, use a type assertion:

f.(RealFoo).Name

or, to avoid panic if it's not a RealFoo :

if realFoo, ok := f.(RealFoo); ok {
    _ := realFoo.Name
}

or a switch over all or some of the possible types of Foo

switch rf := f.(type) {
case realFoo:
    _ := rf.Name // rf is a realFoo
case unrealFoo:
    _ := rf.ImaginaryName // rf is an unrealFoo
case surrealFoo:
    _ := rf.SurrealName // rf is a surrealFoo
default: 
    // rf is none of the above types
}

You could also return a RealFoo from NewRealFoo , instead of a Foo , if you are willing to modify your factory constructor. It will still be valid to use anywhere a Foo is expected, since it implements that interface. That's what you are doing that in NewRealFoo , when returning a RealFoo in a function returning Foo .

func NewRealFoo(name string) RealFoo {
    return &RealFoo{Name: name}
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM