我正在尝试使用null对象模式来实现链表,并且它开始失控。在使用普通null
时,我可以检查节点是否为空,然后决定在其上调用什么。使用null对象模式,必须为每个类型(在本例中为具体节点和具体的null节点)定义我需要在节点上执行的任何操作。当它只有2次操作时,那很好,但是我发现我正在加入,而且它开始变得凌乱:
package tutorial
trait AbNode[+A] {
def getNext[B >: A]: AbNode[B]
def hasNext: Boolean
def replaceTail[B >: A](newNext:AbNode[B]): AbNode[B]
def foreach[B >: A, C](f: B => C): AbNode[C]
def findLast[B >: A]: AbNode[B]
}
case class Node[+A](cargo:A,next:AbNode[A] = NullNode) extends AbNode[A] {
override def getNext[B >: A]: AbNode[B] = next
override def hasNext: Boolean = next != NullNode
override def replaceTail[B >: A](newTail:AbNode[B]): AbNode[B] =
this.copy(next = newTail)
override def foreach[B >: A, C](f: B => C): AbNode[C] =
Node( f(cargo) , next.foreach(f) )
override def findLast[B >: A]: AbNode[B] = next.findLast
}
case object NullNode extends AbNode[Nothing] {
override def getNext[B >: Nothing]: AbNode[Nothing] = NullNode
override def hasNext: Boolean = false
override def replaceTail[B >: Nothing](newCargo:AbNode[B]): AbNode[Nothing] = NullNode
override def foreach[B >: Nothing, C](f: B => C): AbNode[Nothing] = NullNode
override def findLast[B >: Nothing]: AbNode[Nothing] = NullNode
}
//map _ [] = []
//map f (x:xs) = f x : map f xs
class LinkedList[A] {
var head: AbNode[A] = NullNode
var last: AbNode[A] = head
var size: Int = 0
def this(head:AbNode[A], last:AbNode[A]) = {
this
this.head = head
this.last = last
}
def length: Int = size
def maxIndex: Int = size - 1
def pushFront(cargo:A) = {
val oldHead = head
head = Node(cargo).replaceTail(oldHead)
size += 1
}
def pushBack(cargo:A) = {
last.replaceTail(Node(cargo))
last = last.getNext
size += 1
}
def foreach[B](f: A => B): LinkedList[B] = {
val newHead = head.foreach(f)
new LinkedList(newHead, newHead.findLast)
}
def get(index:Int): A = {
var curNode = head
for (i <- 0 to maxIndex) {
curNode = curNode.getNext
}
curNode.cargo // <- Whoops. Now I need to declare getCargo in the AbNode trait, in case curNode is a NullNode
}
}
我使用不正确吗?将东西添加到特征中似乎很荒谬。
答案 0 :(得分:0)
接受所有评论,这就是我所拥有的。之一:
在AbNode中定义当前抽象的方法以使用模式匹配:
trait AbNode[+A] {
def replaceTail[B >: A](newTail:AbNode[B]): AbNode[B] = this match {
case Node(c,n) => Node(c,newTail)
case NullNode => newTail
}
}
使用伴侣对象,并将它们从抽象类中取出:
object Node {
def replaceTail[A](node:AbNode[A], newTail:AbNode[A]): AbNode[A] = node match {
case Node(x,n) => Node(x,newTail)
case NullNode => newTail
}
}
无论哪种方式,模式匹配似乎都是不那么痛苦的解决方案。这种方法的问题是,如果我需要添加一种新类型的节点,我需要更新每个方法(我认为这是@lmm
提到的权衡)。我可以看到自己添加更多方法然后输入类型,所以我会坚持这个。
另一个缺点是,据我所知,如果忽略一个案例,这不会产生编译时错误。以我原来的方式,它会在编译时失败,因为我没有完成这个特性。在这里,它会编译,但如果我忘记了一个案例,可能会因匹配错误而失败。
谢谢你们