【问题标题】:is it bad form to use instance vars directly in ruby?直接在 ruby​​ 中使用实例变量是不好的形式吗?
【发布时间】:2012-04-02 18:18:06
【问题描述】:

您是否应该始终在 ruby​​ 中创建访问器(用于读取和/或写入)?如果你有一个不打算在外面重用的类,我不能直接使用实例变量吗?

我遇到的一个问题是在测试中删除@instance_vars 是有问题的。

【问题讨论】:

标签: ruby coding-style instance-variables


【解决方案1】:

在测试方面,实例变量并不重要。您应该测试您的方法,以验证它们是否产生正确的结果。

当您为属性定义读取器方法时,您将该属性公开给世界。属性的值是否来自实例变量、数据库、文件、运行时计算或其他任何东西都没有关系。人们可以直接调用方法来获取值。

同样,当你为一个属性定义一个 writer 方法时,你是在让每个人都知道他们可以设置它,如果他们需要的话。值的去向无关紧要。

只有您的方法定义您的公共 API。其他一切都是实现细节。

在你的类定义中,直接访问实例变量肯定没有坏处:

@variable = :value

如果您只需要简单的分配,就没有理由调用方法。当然,有时您需要更复杂的功能。延迟初始化,例如:

def variable
  @variable ||= :value
end

# ...

variable.to_s

如果您的方法仅供内部使用,则不应将其包含在您的公共 API 中。将其标记为私有:

private :variable

说实话,Ruby 并没有真正锁定任何东西。即使没有 setter 方法,如果他们真的想要,人们也可以很容易地篡改您的对象:

class << (object = Object.new)
  private
  def variable; @variable end
end

object.variable
# NoMethodError: private method `variable' called

# send bypasses access control
object.send :variable
# => :value

object.instance_variables
# => [:@variable]
object.instance_variable_get :@variable
# => :value

object.instance_variables.each do |variable|
  object.instance_variable_set variable, nil
end
object.instance_variable_get :@variable
# => nil

【讨论】:

  • 如果一个方法使用一个实例变量,我需要能够在测试期间将其存根:stackoverflow.com/questions/9971192/…
  • @m33lky,你不存根变量,你存根方法。我不熟悉mocha,但这是RSpec 中的操作方式:(obj = MyClass.new).stub(:method1).and_return :new_value。之后,obj.method1 将返回 :new_value
  • 抱歉,这是一个不好的例子。我正在对方法本身进行单元测试。它有更多的逻辑,但它取决于一个实例变量。
  • @m33lky,我想我现在明白你所说的实例变量有问题的意思了。在做出断言之前,您需要设置测试对象的状态。将变量设置为所需的值就足够了。属性读取器的好处是,如果您的方法通过一个访问变量,您可以存根读取器方法,并且依赖于它的代码将使用新值。不过,这并不能证明公开内部 API 是合理的。
【解决方案2】:

当您想在设置或执行获取/设置新值的任务之前验证新变量时,我认为访问器更多的是可以帮助您的东西。如果您确定不需要它,我认为最好使用实例变量。否则,您可以在开始时就创建它们,这样当您有大量代码时就不必再创建它们,并且可以节省大量时间。

【讨论】:

    【解决方案3】:

    实例变量应在相关实例的上下文中使用。如果您需要与其他对象甚至其他实例交换数据,您应该根据需要使用attr_readerattr_accessor 公开它们,如果不编写自己的方法来促进它。

    访问器方法充当看门人,并为您提供一个机会来验证外部调用者是否不会弄乱您的内部状态。面向对象设计的一个原则是对象承担筛选输入的责任。如何处理错误输入取决于您自己,无论是忽略它、抛出异常还是记录错误等等。

    如果您不处理错误的输入并因此而崩溃,那么它最终会成为您的“错误”,您将处于堆栈跟踪的顶部。早点拒绝错误值会在问题发生时准确地显示问题,而不是在稍后执行时您可能会忘记该分配的来源。

    通常您不希望人们访问您的数据,除非他们有充分的理由。直接访问和修改另一个对象的实例变量是不好的形式。

    有些语言甚至几乎不可能直接改变对象的内部状态,但 Ruby 在这方面相当随意。尽管如此,仅仅因为可以做某事并不意味着它应该做。

    当您定义访问器时,是否在该实例的实现中使用它们取决于您。有时直接访问它们更方便,调用@var 而不是self.var,但在某些情况下,使用访问器提供了实例变量没有的附加功能。由于您拥有单点控制,因此它还可以让您以后更轻松地重构您的应用程序。

    【讨论】:

      猜你喜欢
      • 2015-12-03
      • 2015-07-07
      • 2015-08-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-04-29
      • 2011-03-30
      相关资源
      最近更新 更多