【问题标题】:Calling child's method from parent's implementation method in golang从golang中父母的实现方法调用孩子的方法
【发布时间】:2017-12-11 17:41:28
【问题描述】:

我正在尝试在 go 中实现一个行为树,但我正在努力使用它的组合功能。基本上,我需要在下面实现Tick() 来调用它所在位置定义的方法。

这里是behavior.go

type IBehavior interface {
  Tick() Status
  Update() Status
}

type Behavior struct {
  Status Status
}

func (n *Behavior) Tick() Status {
  fmt.Println("ticking!")
  if n.Status != RUNNING { n.Initialize() }
  status := n.Update()
  if n.Status != RUNNING { n.Terminate(status) }

  return status
}

func (n *Behavior) Update() Status {
  fmt.Println("This update is being called")
  return n.Status
}

这是嵌入的Behavior 结构:

type IBehaviorTree interface {
  IBehavior
}

type BehaviorTree struct {
  Behavior

  Root IBehavior
}

func (n *BehaviorTree) Update() Status {
  fmt.Printf("Tree tick! %#v\n", n.Root)
  return n.Root.Tick()
}

为了让这个例子更有意义,还有几个文件:

type ILeaf interface {
  IBehavior
}

type Leaf struct {
  Behavior
}

还有这个:

type Test struct {
  Leaf

  Status Status
}

func NewTest() *Test {
    return &Test{}
}

func (n Test) Update() Status {
    fmt.Println("Testing!")
    return SUCCESS
}

下面是它的用法示例:

tree := ai.NewBehaviorTree()
test := ai.NewTest()
tree.Root = test

tree.Tick()

我期待树通过打印这个来正常滴答作响:

ticking!
Tree tick!

但我得到的是:

ticking!
This update is being called

谁能帮我解决这个问题?

编辑:添加了一些额外的文件来说明问题。另外,我不明白反对票。我有一个诚实的问题。我只应该问对我有意义的问题吗?

【问题讨论】:

  • Go 绝对没有继承的概念(嵌入不是继承),你根本不能在 Go 中做父/子的事情。重新设计。
  • 没有继承,但组合确实允许父/子关系;不确定@Volker 在这里得到了什么。无论如何,在您的使用示例中,它仍然没有显示分配给tree.Root 的变量test 是如何创建的。应该是tree.Root = n1 吧?
  • @FelipeRocha 这是一个很好的问题,不要介意反对票。出于某种原因,该站点上的 golang 社区积极地反对问题,通常无论其有效性如何。也许尝试将您的问题简化为单个函数中的独立示例(例如,不需要多个文件)。
  • 不幸的是,它已经没有多大意义了;结构如此复杂和复杂,很难快速跟踪执行情况。这就是将其提炼成MCVE 的好处——通常当您删除一些不必要的复杂性时,问题就会显现出来。

标签: go composition behavior-tree


【解决方案1】:

您的问题是 Tick() 未在您的 BehaviorTree 结构中定义。结果,当您调用tree.Tick() 时,没有定义直接方法,因此它调用了嵌入式Behavior 结构的提升Tick() 方法。 Behavior struct 不知道 BehaviorTree 是什么!在 Go 的伪继承嵌入风格中,“子”类型没有“父母”的概念,也没有任何引用或访问他们。调用嵌入方法时使用 嵌入类型 作为其接收者,而不是嵌入结构。

如果您想要您的预期行为,您需要在您的 BehaviorTree 类型上定义一个 Tick() 方法,并让该方法调用它自己的 Update() 方法(然后调用 sub Tick()Update()方法,如果你愿意)。例如:

type BehaviorTree struct {
  Behavior

  Root IBehavior
}

func (n *BehaviorTree) Tick() Status {
    n.Update() // TODO: do you want this status or the Root.Tick() status?
    return n.Root.Tick()
}

func (n *BehaviorTree) Update() Status {
  fmt.Printf("Tree tick! %#v\n", n.Root)
  return nil
}

【讨论】:

  • 有趣。我试图避免重新定义这种行为......真的没有办法解决吗?
  • 你可以嵌入一个定义方法的接口并调用它。但是不,没有办法调用“子”对象的方法,然后让“子”对象调用“父”对象的方法,除非您有循环引用(即子对象有父母也是)。从嵌入的角度来看,一般的解决方案是拥有一个定义外部方法的结构,并且它嵌入或更常见的简单地具有一个用于保存您要动态定义的方法的接口的字段。
【解决方案2】:

正如沃尔克所说的

Go 完全没有继承的概念(嵌入不是继承),你根本不能在 Go 中做父/子的事情。重新设计

据我所知,您想要的是一个使用接口多次执行相同任务的函数。

func Tick(n IBehavior) Status {
  fmt.Println("ticking!")
  if n.Status != RUNNING { n.Initialize() }
  status := n.Update()
  if n.Status != RUNNING { n.Terminate(status) }
  return status
}

当然 Initialize 必须在界面中。

【讨论】:

    猜你喜欢
    • 2016-06-18
    • 2015-09-04
    • 1970-01-01
    • 2015-03-14
    • 2018-02-25
    • 1970-01-01
    • 2016-09-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多