Ruby私有和公共访问器

wbgh16ku  于 2022-11-04  发布在  Ruby
关注(0)|答案(5)|浏览(198)

在Ruby中定义访问器时,在简洁(我们都喜欢)和最佳实践之间可能存在矛盾。
例如,如果我想公开一个示例上的值,但禁止任何外部对象更新它,我可以执行以下操作:

class Pancake
  attr_reader :has_sauce

  def initialize(toppings)
    sauces = [:maple, :butterscotch]
    @has_sauce = toppings.size != (toppings - sauces).size
...

但是突然间我使用了一个原始示例变量,这让我很紧张。我的意思是,如果我需要在将来的某个日期设置之前处理has_酱料,我可能需要做更多的重构,而不仅仅是覆盖访问器。拜托,原始示例变量?废话。
我可以忽略这个问题,使用attr_accessor。我的意思是,任何人都可以设置属性,如果他们真的想;这毕竟是Ruby,但是我失去了数据封装的概念,对象的接口定义得不太好,系统可能会更加混乱。
另一个解决方案是在不同的访问修饰符下定义一对访问器:

class Pancake
  attr_reader :has_sauce
  private
    attr_writer :has_sauce
  public

  def initialize(toppings)
    sauces = [:maple, :butterscotch]
    self.has_sauce = toppings.size != (toppings - sauces).size
  end
end

这样就完成了任务,但对于一个简单的访问器来说,这是一个样板文件,坦率地说:好恶心
那么,有没有更好、更Ruby的方式呢?

fafcakar

fafcakar1#

private可以接受一个符号arg,所以...

class Pancake
  attr_accessor :has_sauce
  private :has_sauce=
end

class Pancake
  attr_reader :has_sauce
  attr_writer :has_sauce; private :has_sauce=
end

等等。
但是“原始”示例变量有什么问题呢?它们是示例内部的;唯一能用名字来调用它们的代码是pancake.rb中的代码,这是你的。2它们以@开头,我想这会让你说“blech”,这是它们私有的原因。3如果你愿意,可以把@看作private的简写。
至于处理,我想你的直觉是好的:如果可以,请在构造函数中进行处理;如果必须,请在自定义访问器中进行处理。

tyu7yeag

tyu7yeag2#

attr_reader等只是方法-没有理由定义变量供自己使用(我同意你的观点)例如:

class << Object
  def private_accessor(*names)
    names.each do |name|
      attr_accessor name
      private "#{name}="
    end
  end
end

然后使用private_accessor,就像使用attr_accessor一样(我认为您需要一个比private_accessor更好的名称)

bhmjp9jg

bhmjp9jg3#

您可以将attr_reader放在private范围中,如下所示:

class School
  def initialize(students)
    @students = students
  end

  def size
    students.size
  end

  private

  attr_reader :students
end

School.new([1, 2, 3]).students

这将引发预期的错误:

private method `students' called for #<School:0x00007fcc56932d60 @students=[1, 2, 3]> (NoMethodError)
avwztpqn

avwztpqn4#

在类中直接引用示例变量并没有什么错。无论是将这些方法设置为public还是private,attr_accessor都只是间接引用。
在这个特殊的例子中,它可能有助于识别toppings可能是一个您想要保存用于其他目的的属性,而has_sauce是一个“虚拟属性”,一个依赖于底层浇头属性的模型特征。
像这样的东西可能会感觉更干净:

class Pancake
  def initialize(toppings)
    @toppings = toppings
  end

  def has_sauce?
    sauces = [:maple, :butterscotch]
    (@toppings & sauces).any?
  end
end

是否也公开attr_accessor :toppings由您决定。如果您只是把浇头扔掉,那么您的类就不太像Pancake,而更像PancakeToppingDetector;)

相关问题