使用attr_reader与相同实例var名称的私有方法

时间:2016-10-04 23:03:50

标签: ruby

我刚遇到一个用例,我们有一个带有实例变量@errors的类,它通过同名的私有方法存储。

class Foo
  attr_reader :errors

  private

  # memoized method
  def errors
    @errors ||= []
  end
end

f = Foo.new

# private method `errors' called for #<Foo:...
f.errors

这里有什么解决方案?使用#instance_variable_get或重命名属性或方法?

2 个答案:

答案 0 :(得分:1)

我相信您的初始代码表达的意图可以通过以下方式得到最好的满足:不使用attr_reader,而是在公开的封装级别公开该备注方法。

(顺便说一句,受保护的备注方法使attr_reader的定义黯然失色,因此实际上并未满足代码的意图-公共非备忘录访问但私有备忘录访问)。

考虑两种替代方法:

  1. 在:errors阅读器进行某些私有访问之后,总是会发生公共访问。
  2. 在:errors阅读器进行某些私有访问之后,并不总是会发生公共访问。

在情况1中,通过公开记忆的方法并丢弃attr_reader并没有逻辑上的改变;目的仍然实现。 在第2种情况下,由公开备忘录化的方法并删除attr_reader引起的相对于意图的唯一逻辑变化是,在私有访问之前的公共访问现在将进行记忆并返回一个空数组,而不是返回nil。

如果您预期第2种情况,那么您不太可能会依赖于公共访问之前的非记忆性(而不是私有访问),因此您可能会依赖零响应。这是代码的味道(根据Robert Martin的 Clean Code ):最好,如果可能,永远不要传递或返回nil。

如果情况2是一个问题,则最好的后续更改是使依赖于私有访问之前返回null进行公共访问的代码INSTEAD依赖于私有访问之前返回用于公共访问的空数组,与在私有访问之后 进行公共访问的方式相同。如果不可能或不希望这样做,请进一步说明。

答案 1 :(得分:0)

如果您确实希望将阅读器方法保密,则可以使用method_missing将调用重定向到班级中的errors。例如:

class Foo
  attr_reader :errors

  # Add method_missing here
  def method_missing(method_name, *args, &block)
    if method_name.to_s == 'errors'
      errors
    else
      super
    end
  end

  private

  # memoized method
  def errors
    @errors ||= []
  end
end

f = Foo.new

puts f.errors.to_s # => []

但是,正如@SergioTulentsev在评论中发布的那样,这可能会增加代码的复杂性,尤其是当课程已经很大时。