golang插件可以用于工厂功能吗?

时间:2018-12-23 15:49:56

标签: go plugins

我在golang插件模块中有以下代码:

plug.go

package main

import "fmt"

var (
    Thing        = New("first thing")
    ThingFactory = thingFactory{}
)

type thing struct {
    i int
    s string
}

func New(s string) thing {
    return thing{s: s}
}

func (t *thing) Say() string {
    t.i++
    return fmt.Sprintf("%s - %d", t.s, t.i)
}

type thingFactory struct{}

func (t thingFactory) Make(s string) thing {
    return New(s)
}

它被编译为.so对象,并在另一个程序中使用:

main.go

package main

import (
    "fmt"
    "plugin"
)

func main() {
    p, err := plugin.Open("../plug/plug.so")
    if err != nil {
        panic(err)
    }
    symbol, err := p.Lookup("Thing")
    if err != nil {
        panic(err)
    }
    thing := symbol.(Sayer)
    fmt.Println(thing.Say())

    symbol, err = p.Lookup("ThingFactory") // <-problems start here
    if err != nil {
        panic(err)
    }
    factory := symbol.(GetSayer)

    madeThing := factory.Make("how about me?")
    fmt.Println(madeThing.Say())
    fmt.Println(madeThing.Say())
}

type Sayer interface {
    Say() string
}

type GetSayer interface {
    Make(string) Sayer
}

我能够查找Thing,并在其上调用Say(),但是第二次界面转换出现了问题:

first thing - 1 panic: interface conversion: *main.thingFactory is not main.GetSayer: missing method Make

即使运行时将第一个符号识别为Sayer,也无法识别thingFactory显然具有Make()方法,该方法应返回也是Sayer的内容。

我在这里缺少明显的东西吗?

2 个答案:

答案 0 :(得分:2)

第一个问题是插件thingFactory(更确切地说是*thingfactory)在主应用程序的GetSayer界面中没有描述的方法:

Make(string) Sayer

您有:

Make(string) thing

因此(首先),您必须将thingFactory.Make()更改为此:

type Sayer interface {
    Say() string
}

func (t thingFactory) Make(s string) Sayer {
    th := New(s)
    return &th
}

在此之后它仍然无法工作。这是因为插件的Sayer类型与主应用程序的Sayer类型不同。但是,它们必须相同才能实现主应用程序的GetSayer界面。

一种解决方案是将Sayer接口“外包”到其自己的程序包,并在插件和主应用程序中使用该通用共享程序包。

让我们创建一个新程序包,将其命名为subplay

package subplay

type Sayer interface {
    Say() string
}

导入此软件包并在插件中使用:

package main

import (
    "fmt"
    "path/to/subplay"
)

var (
    Thing        = New("first thing")
    ThingFactory = thingFactory{}
)

type thing struct {
    i int
    s string
}

func New(s string) thing {
    return thing{s: s}
}

func (t *thing) Say() string {
    t.i++
    return fmt.Sprintf("%s - %d", t.s, t.i)
}

type thingFactory struct{}

func (t thingFactory) Make(s string) subplay.Sayer {
    th := New(s)
    return &th
}

并在主应用程序中导入并使用它:

package main

import (
    "fmt"
    "path/to/subplay"
    "plugin"
)

func main() {
    p, err := plugin.Open("../plug/plug.so")
    if err != nil {
        panic(err)
    }
    symbol, err := p.Lookup("Thing")
    if err != nil {
        panic(err)
    }
    thing := symbol.(subplay.Sayer)
    fmt.Println(thing.Say())

    symbol, err = p.Lookup("ThingFactory")
    if err != nil {
        panic(err)
    }
    factory := symbol.(GetSayer)

    madeThing := factory.Make("how about me?")
    fmt.Println(madeThing.Say())
    fmt.Println(madeThing.Say())
}

type GetSayer interface {
    Make(string) subplay.Sayer
}

现在它可以工作了,输出将是:

first thing - 1
how about me? - 1
how about me? - 2

查看相关问题:

go 1.8 plugin use custom interface

How do Go plugin dependencies work?

答案 1 :(得分:1)

您的插件Make方法应该返回一个Sayer对象而不是

type Sayer interface {
    Say() string
}
func (t *thingFactory) Make(s string) Sayer {
    return New(s)
}