为了学习Go语言,我试着编写一个可重用的包(比如base
),这个包提供了一个Base
接口,有最少的函数和一个BaseStruct
来实现这些函数。Base
将被嵌入到其他特定于应用领域的包中,比如composite包。
type Base interface {
defaultCfg()
setID(string)
setEnqSize(int)
}
type BaseStruct struct {
id string
enqSize int
}
func New[T Base](t T, opts ...func(T)) T {
// Code is provided in playground below.
}
type Comp interface {
Base
setConn(string)
}
type CompStruct struct {
Base
conn string
}
// Other code in playground.
对我的关键要求:
- 使用一些构造模式(如builder或functional options模式)创建复合结构体的新示例。我能够使用
New
函数获得FOP。 - 使用
defaultCfg
为基础和每个组合提供一组"默认"值,在组合结构的构建过程中可以覆盖这些值。 - 不允许直接修改结构的字段。修改可以通过私有setter进行构造,也可以通过公共消息进行状态更改。
我基本上是在写一个角色模型框架,它使用了一个基于角色树的通信模式,除了在角色启动之前可靠地构造角色的能力之外,这个框架大部分工作(消息传递等)。
作为一个Go语言的新手,我在打包函数时遇到了理解和解决运行时恐慌的困难。
我写了一些最小的代码来隔离这个问题。下面的Playground没有任何演员模型代码,只是重命名了单元来演示我的问题。示例化的工作代码在this playground中。工作代码的输出:
defBase: {id:"DefaultID", enqSize:1}
idBase: {id:"Non-Def-ID", enqSize:1}
myBase: {id:"Non-Def-ID", enqSize:4}
defComp: {Base:{id:"DefaultID", enqSize:1}, conn:"DefaultConn"}
idComp: {Base:{id:"Non-Def-ID", enqSize:1}, conn:"DefaultConn"}
myComp: {Base:{id:"Non-Def-ID", enqSize:4}, conn:"Non-Def-Conn"}
Program exited.
然而,当我像this non-working playground那样重新打包代码时,即使在构造默认的复合结构时,代码也会运行到异常状态。输出(前两行对我来说只是一个检查):
Type of BaseStruct is: base.BaseStruct
Type of CompStruct is: comp.CompStruct
defBase: {id:"DefaultID", enqSize:1}
idBase: {id:"Non-Def-ID", enqSize:1}
myBase: {id:"Non-Def-ID", enqSize:4}
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x18 pc=0x482f44]
goroutine 1 [running]:
play.ground/comp.(*CompStruct).play.ground/base.defaultCfg(0x1?)
<autogenerated>:1 +0x24
play.ground/base.withDefault[...].func1()
/tmp/sandbox109723629/base/base.go:16 +0x2f
play.ground/base.New[...]({0x4bafe0?, 0xc00006e060}, {0x0, 0x0, 0x1})
/tmp/sandbox109723629/base/base.go:6 +0x47
main.main()
/tmp/sandbox109723629/prog.go:34 +0x379
Program exited.
我有一个变通方法,取自Dave Cheney,其中New
函数在comp包中也会重复。
func NewComp(options ...func(Comp)) Comp {
var e Comp = &CompStruct{
Base: base.NewBase(),
conn: "DefaultConn",
}
for _, option := range options {
option(e.(Comp))
}
return e.(Comp)
}
但是,我希望避免为每个依赖于Base(或另一个复合)的复合包创建New的变体。参考两个领域中的完整代码,我还希望避免为每个WithXxx
选项指定泛型的示例;并让它使用为New
函数指定的示例,这可能吗?
任何修复的指针和任何编写代码或更好的这篇文章的建议都是非常欢迎的。
谢谢你。
1条答案
按热度按时间t40tm48m1#
由于CompStruct.Base字段为
nil
,程序出现异常。请通过将所有使用new(comp.CompStruct)
的情况更改为&comp.CompStruct{Base: &base.BaseStruct{}}
来初始化该字段。