【问题标题】:Should we add attr_* methods in Ruby when creating a Class?我们应该在创建类时在 Ruby 中添加 attr_* 方法吗?
【发布时间】:2020-10-19 22:47:27
【问题描述】:

我在 Ruby 中创建类,没有任何 attr_* 方法,如 attr_reader、attr_writer 或 attr_accessor。 所以像这样的

class DocumentIdentifier
 def initialize( folder, name )
  @folder = folder
  @name = name
 end
end

我的意思是它可以像这样被实例化

di = DocumentIdentifier.new(folder, 'image')

但我在一些 ruby​​ 书籍中阅读了一些代码,它们也像这样将 attr_* 添加到实例变量中

class DocumentIdentifier
 attr_reader :folder, :name
 def initialize( folder, name )
  @folder = folder
  @name = name
 end
end

我的问题是,我们应该在每个类定义中使用 attr_* 吗?原因是什么?

【问题讨论】:

  • 这是一个通用的 OOP 问题:当我们有一个带有一些实例变量的类时——在什么情况下我们应该提供 getter 和 setter 方法?这不是特定于 Ruby 的,并且已经讨论过这个问题,例如 herehere
  • 这能回答你的问题吗? Explain to me what is a setter and getter
  • 是的,也许这是一般的 oop 问题,但我也想知道是否有一些红宝石的答案,但这是一个一般的 oop 问题,所以是的,你是对的 @user1934428

标签: ruby


【解决方案1】:

attr_reader 将创建返回具有匹配名称的实例变量的方法。

class Foo
  attr_reader :bar

  def initialize(bar)
    @bar = bar
  end
end

这意味着Foo 的实例在内部和外部都将具有bar 方法,该方法将响应实例变量@bar 的值:

foo = Foo.new(1)
foo.bar
=> 1

依赖行为(方法)比依赖数据(直接实例变量)要好。

如果您通过attr_reader 生成的方法访问数据,您可以确保通过将行为封装在同一个地方,您的代码会稍微“面向未来”,因此最好访问所有你的实例变量通过它们

另一个有趣的方面是您可以限制界面以满足您的需求。例如,如果您只在内部使用,则无需将bar 暴露给外界,因此:

class Foo
  def initialize(bar)
    @bar = bar
  end

  def zoo
    bar
  end


  private

  attr_reader :bar
end

会表现得像:

foo = Foo.new(1)
foo.zoo
=> 1

但是:

foo.bar
NoMethodError (private method `bar' called for #<Foo:0x00005581544b83d8 @bar=1>)

请让我知道这是否有意义,否则我将更新答案并提供更多详细信息。

【讨论】:

  • 我确实明白你的解释,所以根据你的回答,我认为这取决于我们将对类或对象做什么,如果我们想公开实例变量以便我们可以使用 attr_reader或任何 attr_。如果我们不想要它,请不要使用 attr_ 或将其设为私有
  • 我的意思是,即使您不想在外部公开它,仍然要使用它,只是将其设为私有,因此即使在内部,您的类也有一个通用接口来访问变量。如果您认为可以接受,请您接受答案吗?谢谢!
【解决方案2】:

attr_reader 是一个“getter 方法”。换句话说,它允许我们声明数据属性将是公开可读的。

当您在类的实例上保存数据属性时,默认情况下无法从类外部查看该数据。通过设置attr_reader,我们可以从任何地方访问该属性。

这是一个使用您问题中的“名称”字段的示例。

# without "attr_reader"
class DocumentIdentifier
   def initialize(folder, name)
      @folder = folder
      @name = name
  end
end

first_example = DocumentIdentifier.new(nil, 'test')

first_example.name

这会报错:

Traceback (most recent call last):
        4: from /Users/ibell/.rvm/rubies/ruby-2.6.5/bin/irb:23:in `<main>'
        3: from /Users/ibell/.rvm/rubies/ruby-2.6.5/bin/irb:23:in `load'
        2: from /Users/ibell/.rvm/rubies/ruby-2.6.5/lib/ruby/gems/2.6.0/gems/irb-1.0.0/exe/irb:11:in `<top (required)>'
        1: from (irb):9
NoMethodError (undefined method `name' for #<DocumentIdentifier:0x00007fe0160956e8 @folder=2, @name="l">)

现在,如果您添加 attr_accessor,同样的代码也可以使用。

class DocumentIdentifier
  attr_reader :name
  def initialize(folder, name)
    @folder = folder
    @name = name
  end
end

second_example = DocumentIdentifier.new(nil, 'test')

second_example.name

这会给你返回字符串“test”。

正如我们需要读取数据一样,我们可能也想写入数据。 attr_writerattr_reader 的工作方式类似,但它允许我们“设置”数据。

class DocumentIdentifier
  attr_writer :name
  def initialize(folder, name)
    @folder = folder
    @name = name
  end
end

second_example = DocumentIdentifier.new(nil, 'test')

second_example.name = 'we can change this text'

second_example.name

这会给你回:'we can change this text'

最后,attr_accessor 是当您希望 attr_readerattr_writer 具有相同属性时的快捷方式。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-09-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多