本文首发于我的个人博客
本文记录了作者在golang开发中,通过抽取接口,依赖注入的方式,解决包与包之间的不合理引用关系。
总结来说:
面向接口编程,并且golang中接口函数的参数最好是标准库的类型
场景
business_logicpkg1pkg2
pkg1pkg2business_logicpkg1pkg2pkg1pkg2
// pkg1/main.go
package pkg1
import "pkg2"
func ExternalAPI() {
pkg2.ExternalAPI(pkg2.S{})
}
// pkg2/main.go
package pkg2
type S struct {
param1 int
}
func ExternalAPI(s S) {
}
// business_logic/main.go
package main
import (
"pkg1"
"pkg2"
)
func main() {
pkg1.ExternalAPI()
pkg2.ExternalAPI(pkg2.S{})
}
这样就引起了一个问题:
business_logicpkg2pkg1
解决尝试
pkg1pkg2
pkg1pkg2pkg1
pkg2pkg1business_logicpkg2pkg1
但是这样的尝试失败了,我们先来看一下代码
// pkg1/main.go
package pkg1
import "pkg2"
type Plugin interface {
ExternalAPI(s pkg2.S)
}
var plugin Plugin
func ExternalAPI() {
if plugin != nil {
plugin.ExternalAPI(pkg2.S{})
}
}
func SetPlugin(p Plugin) {
plugin = p
}
// pkg2/main.go
package pkg2
type S struct {
param1 int
}
type Plugin struct {
}
func (p *Plugin) ExternalAPI(s S) {
}
func ExternalAPI(s S) {
p := Plugin{}
p.ExternalAPI(s)
}
// business_logic/main.go
package main
import (
"pkg1"
"pkg2"
)
func main() {
pkg1.SetPlugin(&pkg2.Plugin{})
pkg1.ExternalAPI()
pkg2.ExternalAPI(pkg2.S{})
}
pkg1pkg2pkg2
type Plugin interface {
ExternalAPI(s pkg2.S)
}
最终解决方案
pkg2
// pkg1/main.go
package pkg1
type Plugin interface {
ExternalAPI(param int)
}
var plugin Plugin
func ExternalAPI() {
if plugin != nil {
plugin.ExternalAPI(0)
}
}
func SetPlugin(p Plugin) {
plugin = p
}
// pkg2/main.go
package pkg2
type Plugin struct {
}
func (p *Plugin) ExternalAPI(s int) {
}
func ExternalAPI(s int) {
p := Plugin{}
p.ExternalAPI(s)
}
// business_logic/main.go
package main
import (
"pkg1"
"pkg2"
)
func main() {
pkg1.SetPlugin(&pkg2.Plugin{})
pkg1.ExternalAPI()
pkg2.ExternalAPI(0)
}
pkg1pkg2pkg2.S
视具体业务情况而定,我们可以通过:
map[string]interface{}stringpkg3pkg3
软件开发中没有silver-bullet,只有trade-off,这次的方案,也还算满意