一尘不染

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

go

作为学习Go的练习,我正在编写一个基本的Queue数据结构。我昨天开始学习有关接口的知识,我认为尝试将其用于本练习将很酷。我要完成的工作是拥有一个Queue可以接受实现此接口的任何类型的对象:

type Queuable interface {
  Next() *Queuable  // This is probably not right
}

基本上,我想要的是能够将任何具有Next()方法的类型添加到我的Queue。所以我尝试的是:

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
}

// Next gets the next object
func (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 Queue
func (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
}

阅读 189

收藏
2020-07-02

共1个答案

一尘不染

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

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](http://play.golang.org/p/2RCRpV_NBX)

还要注意的是有一些代码重复NodeJob,作为nextNext()SetNext()方法。我们可以创建一个基本节点实现,例如:

type Base struct {
    next Queuable
}

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

现在你可以嵌入这个Base在您的具体类型NodeJob实现其将“继承”的next领域,Next()SetNext()方法,所以你不必定义对任何这些NodeJob类型。

这是全面落实NodeJob,没有别的要求:

type Node struct {
    *Base
    value interface{}
}

type Job struct {
    *Base
    instruction string
}

在上尝试一下[Go Playground](http://play.golang.org/p/Fv9HbYJyhQ)

2020-07-02