【问题标题】:why does Ruby throw warnings for private attributes为什么Ruby会为私有属性抛出警告
【发布时间】:2013-08-19 07:36:01
【问题描述】:

以示例类为例:

# in ./example.rb
class Example
  private
    attr_accessor :name
end

当我在详细模式下运行它时,Ruby 会向我抛出警告:

$ ruby -W2 ./example.rb
example.rb:3: warning: private attribute?

为什么不建议这样做?

【问题讨论】:

  • 我不知道为什么 Ruby 团队不推荐私有 attr_accessor 方法,但我认为它们比使用实例变量更可取,因为后续更改更​​容易。 Sandi Metz 也这么认为:rubyrogues.com/…
  • 警告已在 Ruby 2.3 中删除,请参阅 bugs.ruby-lang.org/issues/10967

标签: ruby private language-design


【解决方案1】:

因为在大多数情况下,定义从外部看不到的 getter/setter 没有多大意义。我们通常使用attr_accessor 只是为了在类之外公开一个实例变量。但是,private 关键字使生成的 getter/setter 方法对外界不可见,从而破坏了这一目的。

您想要使用私有 setter/getter 的唯一原因是涉及到一些额外的逻辑。但是,在这种情况下,无论如何,您都必须使用 def 手动定义这些方法。

【讨论】:

  • 我经常使用私有的 getter/setter。我宁愿使用foo 而不是@foo,因为如果我发现我需要一个默认值或其他东西,我可以def foo 而不必更改所有引用。二传手也一样。
  • 这就是我最后一段的意思:在这种情况下,你必须def foo,因为你有额外的逻辑。
  • 抱歉,我不清楚。我的意思是,如果您使用 @foo 并发现需要额外的逻辑,则必须在任何地方更改 @foo。而如果你使用private; attr_accessor :foo 并使用foo,如果你发现你需要额外的逻辑,你只需要def foo,而不是搜索和替换@foo
  • OOP 中的一个很好的原则是始终使用您的方法,即使是内部使用。在这个名称属性的情况下,假设将来它会变得比属性更复杂。如果你使用了@name,那么你需要用对name 方法的调用来替换它们。因此,我很惊讶 Ruby 会抛出这个警告。 IMO,这似乎与好的 OOP 建议相反。
  • 同意,这是 OOP 的基础——封装。我总是对我的数据使用私有或受保护(用于比较)attr_readers - 遵循经验法则:如果您想查看对象的数据,那么您最好将此行为放在具有此数据的对象内部。这样,如果数据的内部表示发生变化,我就不需要修复数百个文件和测试套件。或者,如果对象开始承担太多责任(~> 一),则将数据移动到具有所有相关行为的不同对象中。
【解决方案2】:

虽然我已经接受@padde 的回答,但我还是想分享一些代码以供将来参考。

我想查看@Babai关于属性方法的默认访问级别的回答。

下面是它的工作原理。我将在2.0.0-p247的源上进行演示。

这是attr_accessor的来源:

static VALUE
rb_mod_attr_accessor(int argc, VALUE *argv, VALUE klass)
{
    int i;

    for (i=0; i<argc; i++) {
rb_attr(klass, rb_to_id(argv[i]), TRUE, TRUE, TRUE);
    }
    return Qnil;
}

如您所见,它为每个参数调用rb_attr 函数。 (我猜argc 代表argument counter。)所以我们必须查看rb_attr 源来了解这一切是如何工作的:

void
rb_attr(VALUE klass, ID id, int read, int write, int ex)
{
    const char *name;
    ID attriv;
    VALUE aname;
    rb_method_flag_t noex;

    if (!ex) {
      noex = NOEX_PUBLIC;
    }
    else {


      if (SCOPE_TEST(NOEX_PRIVATE)) {
        noex = NOEX_PRIVATE;
        rb_warning((SCOPE_CHECK(NOEX_MODFUNC)) ?
          "attribute accessor as module_function" :
          "private attribute?");
      }
      else if (SCOPE_TEST(NOEX_PROTECTED)) {
        noex = NOEX_PROTECTED;
      }
      else {
        noex = NOEX_PUBLIC;
      }
    }

    /* more logic that's not relevant for this explanation */
}

如您所见,解释器检查访问级别是否为NOEX_PRIVATE,如果是则引发错误。

【讨论】:

  • 好一个..!朋友..我喜欢你的努力.. :)
猜你喜欢
  • 2014-09-25
  • 1970-01-01
  • 1970-01-01
  • 2021-07-27
  • 1970-01-01
  • 1970-01-01
  • 2018-02-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多