为什么attr_accessor不能在设置方法方面工作

时间:2017-12-26 02:45:52

标签: ruby class oop getter-setter object-oriented-analysis

我在ruby中实现BST并使用递归编写插入方法。

我正在尝试使用attr_accessor来设置和获取root,但它不起作用。任何人都可以帮忙吗?

class Node 
  attr_accessor :value, :left_child, :right_child

  def initialize (value)
    @value = value 
    @left_child = nil 
    @right_child = nil 
  end
end 

class BST 
  attr_accessor :root 

  def initialize
    @root = nil
  end 



  def insert(value, node)

    if node == nil 
      node = Node.new(value)
      return node
    end 

    return node if node.value == value

    if node.value > value
      if node.left_child == nil 
        node.left_child = Node.new(value)
        return
      else 
        insert(value, node.left_child) 
      end 

    else

      if node.right_child == nil 
        node.right_child = Node.new(value)
        return
      else 
        insert(value, node.right_child) 
      end
    end 
  end 
end 


mybst = BST.new
p mybst.root
mybst.insert(1, mybst.root)
mybst.insert(2, mybst.root)
mybst.insert(10, mybst.root)
mybst.insert(12, mybst.root)

p mybst

上面的代码显示了Node类的简单实现和带有insert方法的BST类。给了我#<BST:0x00557398d02378 @root=nil>

如果我使用self.root它可以工作。

可以使用@root访问root,但是类不应该直接与其实例变量进行交互。这就是我们需要attr_accessor提供的getter和setter方法的原因。但它不起作用。我缺少什么?

以下是POODR一书的截图。它表示即使在课堂上也不要直接使用实例变量。

enter image description here enter image description here

1 个答案:

答案 0 :(得分:2)

实际上完全可以在实例方法中使用实例变量。事实上,这就是他们的目标! Setter和getter允许实例外部的东西访问实例内的变量。他们(基本上)定义类的实例方法,如下所示:

class Foo
  # getter -- Same as attr_reader :root
  def root
    @root
  end

  # setter -- Same as attr_writer :root
  def root=(root)
    @root = root
  end

  # attr_accessor defines a setter *and* a getter.
end

因此,您可以通过定义#insert来简化代码,以便只需要一个参数(value)并将node引用的每个位置替换为{{1} }}

我认为您正在寻找的方式(但不是&#34;正确的方式,而且我不建议)是致电@root和{ {1}}访问者定义的方法。

如果您采用此路线,则还必须定义root以仅将root=作为参数,并将#insert引用的每个地点替换为{{1} }}。这样可行,但这不是解决问题的正确方法。如果你这样解决,请在CodeReview.se上提问,这样我就可以澄清如何使代码更好。

为什么它无法正常工作

在#insert方法中,您将操纵传递给方法的value参数,而不是node。 Ruby是按值传递而不是通过引用传递(sorta),因此当您将root传递给node时,您有效地传递root,因为mybst.root。然后,#insert调用会返回一个新节点,但您不会对该返回值执行任何操作。如果您想将nil设置为该返回值,则可以执行以下操作:

mybst.root == nil

说明该教科书尝试

我认为这里令人困惑的部分是教科书所说的内容:

  

隐藏变量,甚至是从定义变量的类

这是正确的,但我认为你错误地解释了它。本节说您应该隐藏实例变量来自该实例之外的任何内容。在那个实例中,使用它们是完全可以的,实际上存在实例变量的原因 - 在实例中存储状态。仅仅考虑更好地定义行为方法而不是直接暴露实例变量。当然,这只是一个要记住的规则 - 我确定您会遇到这种建议不适用的情况,但通常您希望将实例变量保持在内部。