以下示例是基本的节点层次结构实现,我想将其嵌入到其他结构中:
package main
import "fmt"
type NodeInterface interface {
AddChild(child NodeInterface)
RemoveChild(child NodeInterface)
RemoveFromParent()
setParent(parent NodeInterface)
}
type Node struct {
parent NodeInterface
children []NodeInterface
}
func (n *Node) setParent(parent NodeInterface) {
n.parent = parent
}
func (n *Node) AddChild(child NodeInterface) {
n.children = append(n.children, child)
child.setParent(n)
}
func (n *Node) RemoveChild(child NodeInterface) {
found := false
var index int
for i, node := range n.children {
if node == child {
found = true
index = i
break
}
}
if found {
n.children = append(n.children[:index], n.children[index+1:]...)
} else {
panic(fmt.Sprintf("child not found; given: %T; existing: %T", child, n.children[0]))
}
}
func (n *Node) RemoveFromParent() {
n.parent.RemoveChild(n)
}
func main() {
apex := &Node{}
cl := &Node{}
apex.AddChild(cl)
cl.RemoveFromParent()
}
所以,当我尝试这个时:
type Foo struct {
Node
}
func main() {
apex := &Foo{}
cl := &Foo{}
apex.AddChild(cl)
cl.RemoveFromParent()
}
它惊慌失措:
child not found; given: *main.Node; existing: *main.Foo
——这是可以理解的。
但是如何让它发挥作用呢?我确实希望将形成层次结构的能力引入任意结构,例如
Foo
。
这是一个棘手的问题。
首先让我们看一个简单的方法声明:
package main
import "fmt"
type Node struct{}
type NodeInterface any
func (receiver *Node) Method(parameter NodeInterface) {
var ni NodeInterface = receiver
_, rok := ni.(*Foo)
_, pok := parameter.(*Foo)
fmt.Printf("I'm a %T (castable to *Foo: %v) called with a %T (castable to *Foo: %v)\n", ni, rok, parameter, pok)
}
type Foo struct {
Node
}
func main() {
apex := &Foo{}
apex.Method(apex)
}
这给出了 “我是一个
*main.Node
(可转换为 *Foo
:假),用 *main.Foo
(可转换为 *Foo
:真)”,在 Go Playground 上尝试一下。
好吧,我们知道 - apex.Method(apex)
是
(&(*apex).Node).Method(apex)
的语法糖。即使接收者是嵌入的,你也不能向下转型,通过指向内部结构的指针会丢失该信息。
另一方面接口保持原有类型。
有效的方法是
func main() {
apex := &Foo{}
cl := &Foo{}
apex.AddChild(&cl.Node)
cl.RemoveFromParent()
}
这是的语法糖
func main() {
apex := &Foo{}
cl := &Foo{}
(&(*apex).Node).AddChild(&(*cl).Node)
(&(*cl).Node).RemoveFromParent()
}
在这里您可以添加和删除 NodeInterface(&(*cl).Node)
(或者
NodeInterface(&cl.Node)
,如果您愿意的话),这样就可以了。另一方面,
NodeInterface(cl)
是不同的值并且不相等。尝试举例
func (Foo) RemoveFromParent() {
fmt.Println("I would prefer rather not to")
}
func main() {
apex := &Foo{}
cl := &Foo{}
apex.AddChild(cl)
cl.RemoveFromParent()
}
根本不调用cl.Node.RemoveFromParent
。