为了账号安全,请及时绑定邮箱和手机立即绑定

使用接口为任意类型创建队列

使用接口为任意类型创建队列

Go
陪伴而非守候 2021-12-20 09:41:43
作为学习 Go 的练习,我正在编写一个基本的队列数据结构。我昨天开始学习接口,我认为在这个练习中尝试使用它们会很酷。我想要完成的是拥有一个Queue可以接受实现此接口的任何类型:type Queuable interface {  Next() *Queuable  // This is probably not right}基本上我想要的是能够将任何具有Next()方法的类型添加到我的Queue. 所以我尝试的是:type Node struct {    value interface{}    next  *Queuable}// Next gets the next objectfunc (n *Node) Next() *Queuable {    return n.next}// Job - A job for the queuetype Job struct {    instruction string    next        *Queuable}// Next gets the next objectfunc (j *Job) Next() *Queuable {    return j.next}// Queue ...type Queue struct {    head *Queuable    size int}我的方法看起来像:func (q *Queue) Enqueue(node *Queuable) {    ...}// Dequeue - Remove a Queueable form the Queuefunc (q *Queue) Dequeue() *Queuable {  result := q.head  q.head = q.head.Next()  q.size--  return result}我收到了很多这样的错误(基本上在任何有任务的行上):current.Next undefined (type *Queuable is pointer to interface, not interface)所以最终我想做的是:func main() {  queue := NewQueue()  // Helper function not pictured  job := &Job{"some instructions", nil}  node := &Node{5, nil}  queue.Enqueue(node)  // queue = [node]  queue.Enqueue(job) // queue = [node, job]  queue.Dequeue() // node  queue.Dequeue() // job}
查看完整描述

2 回答

?
蛊毒传说

TA贡献1895条经验 获得超3个赞

不要使用指向接口类型的指针,只使用接口类型。


Queuable是一种接口类型,因此在您使用 的代码中的任何地方*Queuable,都将其更改为Queuable. 例如:


type Queuable interface {

    Next() Queuable

}


type Node struct {

    value interface{}

    next  Queuable

}


// Next gets the next object

func (n *Node) Next() Queuable {

    return n.next

}


...

在 Go 中,接口类型的值存储一对:分配给变量的具体值,以及该值的类型描述符。


更多关于接口的内部结构:反射定律#接口的表示


所以你几乎不需要一个指向接口的指针。一个接口包含一个键值对,其中键可以是一个指针。接口指针有意义的罕见情况是,如果您想修改传递给另一个函数的接口类型变量的值。


在您的示例中,该类型*Job实现了Queuable因为它有一个带有接收器类型的方法*Job,因此在需要值的任何地方Queuable,*Job都可以使用值(并且Queuable将创建和使用类型的隐式接口值)。


回到你的例子:


您Queuable只定义了一种方法来获取队列中的下一个元素,但没有定义一种方法来将它排入队列,这将使该解决方案失去灵活性。单个Next()方法仅描述它是“排队的”,但它不是(必然)“可排队的”。


为了排队,我还要添加另一种方法:SetNext(Queuable)


type Queuable interface {

    Next() Queuable

    SetNext(Queuable)

}

它的实现Node可以是例如:


func (n *Node) SetNext(q Queuable) { n.next = q }

在Go Playground上试试。


另请注意,Nodeand中有一些代码重复Job,即next字段Next()和SetNext()方法。我们可以创建一个基本节点实现,例如:


type Base struct {

    next Queuable

}


func (b *Base) Next() Queuable     { return b.next }

func (b *Base) SetNext(q Queuable) { b.next = q }

现在您可以将这种Base类型嵌入到将“继承”字段和方法的具体Node和Job实现中,因此您不必在和类型上定义任何这些。nextNext()SetNext()NodeJob


这是Nodeand的完整实现,Job不需要其他任何东西:


type Node struct {

    *Base

    value interface{}

}


type Job struct {

    *Base

    instruction string

}

在Go Playground上试试这个。


查看完整回答
反对 回复 2021-12-20
?
慕的地6264312

TA贡献1817条经验 获得超6个赞

永远不要使用指向接口类型的指针,这已经是一个指针了!


因此,要使代码正常工作,请更改*Queuableas Queuable。


type Node struct {

    value interface{}

    next  Queuable

}


// Next gets the next object

func (n *Node) Next() Queuable {

    return n.next

}


// Job - A job for the queue

type Job struct {

    instruction string

    next        Queuable

}

但是,您可以使用方法接收器作为指针,具体取决于结构的复杂性。虽然如果使用的 struct 类型很简单,您可以定义使用 struct 值的方法,这种方式在内存中分配一个新地址。如果您使用方法接收器作为指针,它将引用该结构已在内存中占用的地址。


关于接收者的指针与值的规则是值方法可以在指针和值上调用,但指针方法只能在指针上调用。


出现这个规则是因为指针方法可以修改接收者;在一个值上调用它们将导致该方法接收该值的副本,因此任何修改都将被丢弃。因此,该语言不允许这种错误。


经验法则是,为了一致性,最好坚持将方法定义作为指针或方法定义作为整个接口实现中的值。


查看完整回答
反对 回复 2021-12-20
  • 2 回答
  • 0 关注
  • 173 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号