我刚遇到一个用例,我们有一个带有实例变量@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
或重命名属性或方法?
答案 0 :(得分:1)
我相信您的初始代码表达的意图可以通过以下方式得到最好的满足:不使用attr_reader,而是在公开的封装级别公开该备注方法。
(顺便说一句,受保护的备注方法使attr_reader的定义黯然失色,因此实际上并未满足代码的意图-公共非备忘录访问但私有备忘录访问)。
考虑两种替代方法:
在情况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在评论中发布的那样,这可能会增加代码的复杂性,尤其是当课程已经很大时。