作为学习Go的练习,我正在编写一个基本的Queue数据结构。我昨天开始学习有关接口的知识,我认为尝试将其用于本练习将很酷。我要完成的工作是拥有一个Queue可以接受实现此接口的任何类型的对象:
Queue
type Queuable interface { Next() *Queuable // This is probably not right }
基本上,我想要的是能够将任何具有Next()方法的类型添加到我的Queue。所以我尝试的是:
Next()
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 }
不要使用指向接口类型的指针,而只需使用接口类型。
Queuable是接口类型,因此在您使用过的代码中的任何地方都*Queuable将其更改为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将创建和使用类型的隐式接口值)。
*Job
回到您的示例:
您Queuable只定义了一种方法来获取队列中的下一个元素,但没有一个方法将其排队,这会使该解决方案失去灵活性。单个Next()方法仅描述它是 “排队的”, 但不是(必需) “排队的” 。
为了 排队, 我还要添加另一种方法:SetNext(Queuable)
SetNext(Queuable)
type Queuable interface { Next() Queuable SetNext(Queuable) }
其实现Node例如可以是:
Node
func (n *Node) SetNext(q Queuable) { n.next = q }
在上尝试一下[Go Playground](http://play.golang.org/p/2RCRpV_NBX)。
[Go Playground](http://play.golang.org/p/2RCRpV_NBX)
还要注意的是有一些代码重复Node和Job,作为next场Next()和SetNext()方法。我们可以创建一个基本节点实现,例如:
Job
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实现其将“继承”的next领域,Next()和SetNext()方法,所以你不必定义对任何这些Node和Job类型。
Base
这是全面落实Node和Job,没有别的要求:
type Node struct { *Base value interface{} } type Job struct { *Base instruction string }
在上尝试一下[Go Playground](http://play.golang.org/p/Fv9HbYJyhQ)。
[Go Playground](http://play.golang.org/p/Fv9HbYJyhQ)