【问题标题】:Why does the instance method "protected_class_method" not exist for the "Module" class in Ruby?为什么Ruby中的“模块”类不存在实例方法“protected_class_method”?
【发布时间】:2018-07-09 19:44:04
【问题描述】:

为什么 Ruby 中“Module”类没有实例方法“protected_class_method”,而“Module”类却存在“public_class_method”和“private_class_method”实例方法?

这不遵循为“模块”类定义的“私有”、“受保护”和“公共”实例方法的模式。

【问题讨论】:

  • “私人”和“公共”之间的区别取决于谁在问,但我不确定 Ruby 的反射功能是否始终正确分类事物。 “受保护”只是“对某些人来说是公开的,对其他人来说是私人的”的一种说法。
  • Matz says, “如果您了解受保护可见性的当前行为,您就会明白为什么 protected_class_method 根本没有用。这就是我们没有它的原因。”

标签: ruby


【解决方案1】:

对我来说,受保护的方法仅作为实例方法才有意义。

受保护的方法可以被同一类的其他实例调用。

class Student
  def initialize(age)
    @age = age
  end
  def older_than?(other)
    age > other.age
  end
  protected
  def age
    @age
  end
end

您不能直接在 Student 实例上调用 age

student1 = Student.new(21)

student1.age
NoMethodError: protected method `age' called for #<Student:0x514d058 @age=21>

但是 student1 可以参考 student2 的年龄

student2 = Student.new(23)

student2.older_than?(student1)
 => true

所以你可以看到一个实例的受保护方法的唯一性是如何被另一个实例引用的。

我看不出你会如何使用类“受保护的方法”......没有类似于上述的场景。

编辑

感谢 Cary Swoveland 完全弄乱了我的想法,我意识到您可以执行以下操作...

class Class
  def show_k(klass)
    klass.k
  end
  protected
  def k
    "This is k"
  end
end

如果我这样做了

String.k 
NoMethodError: protected method `k' called for String:Class

但如果我这样做......

Integer.show_k(String)
=> "This is k"

可能因为类是 Class 类的实例。

我仍然不确定我会如何使用它,但是你去吧。

【讨论】:

  • @CarySwoveland 这是我以前从未考虑过的事情。我的另一个问题是,“模块”类上的“protected_class_method”实例方法是否会被答案中编辑中描述的方法变得多余
  • protected_class_method 在课堂上仍然没有意义。 EDIT 代码为所有类创建了一个受保护的方法。好吧,所有继承自 Class 的类,这几乎是所有标准类的设计,也是新类的默认设置。你是否想要这种全球影响是值得商榷的。
  • @SteveTurczyn 就编辑代码的影响而言,恕我直言,全球影响将是不可取的。我在想的是,如果 Module 类上有一个“protected_class_method”实例方法,那么您可以有一个方法(每个类都有不同的实现),该方法只能由其他类在类上调用,也许是为了获取元数据来自其他班级(但这对IMO没有用)。示例:class A; protected_class_method def self.m; :metadata; end; endclass B; def self.p(a); puts a.class.m; end; end,其中A.m 会失败,但B.p(A.new) 不会。
  • 我只能看到对 Class 的修改才有效。它不适用于子类(例如,您想创建一个类 ProtectedMethod 和类 Dog 和类 Cat,它们都继承自 ProtectedMethod),因为子类中的受保护方法要么是实例方法,要么不受保护。所以,不,不认为这是可能的,比我聪明的人可能会找到办法。
  • @CarySwoveland 好的,但不要让它再过八年... :)
【解决方案2】:

让我们用一个类方法定义一个类。

class K
  def self.k
    'hi'
  end
end

def self.k 只是在K 的单例类上定义实例方法 k 的简写方式。让我们这样做并使其受到保护。

class K
  class << self
    protected def k
      puts 'hi'
    end
  end
end

我们发现

K.k
  #=> NoMethodError: protected method `k' called for K:Class

调用此方法的唯一方法是(与私有方法一样)是使用隐式接收器:

class K
  k
end
  #=> 'hi'

根据定义,在单例类上定义的受保护实例方法可以由同一类的实例调用,但不能创建单例类的实例。

这种行为与私有类方法相同,因此拥有受保护的方法也无济于事。

【讨论】:

  • 如果您考虑使用显式接收器继承和调用类方法,则此答案的最后一行不是 100% 正确。
  • 请参阅stackoverflow.com/a/48879939/9290781,了解在类方法上下文中可能使用的protected 用例。
【解决方案3】:

如果您考虑到类方法中显式接收器的继承和使用,则在声明类方法(类的单例类上的实例方法)时,受保护和私有之间存在差异。以下示例使用 Ruby 2.5 进行了测试。

class A
  class << self
    protected def pro
      puts 'pro'
    end
  end
end

class B < A
  class << self
    # Works
    def explicit_receiver_test
      self.pro
    end

    # Works
    def implicit_receiver_test
      pro
    end
  end
end

begin
  B.pro # Throws: NoMethodError
rescue NoMethodError => e
  puts e.message # Prints: protected method `pro' called for B:Class
end
B.explicit_receiver_test # Prints: pro
B.implicit_receiver_test # Prints: pro

class C
  class << self
    private def pri
      puts 'pri'
    end
  end
end

class D < C
  class << self
    # Fails
    def explicit_receiver_test
      self.pri
    end

    # Works
    def implicit_receiver_test
      pri
    end
  end
end

begin
  D.pri # Throws: NoMethodError
rescue NoMethodError => e
  puts e.message # Prints: private method `pri' called for D:Class
end
begin
  D.explicit_receiver_test # Throws: NoMethodError
rescue NoMethodError => e
  puts e.message # Prints: private method `pri' called for D:Class
end
D.implicit_receiver_test # Prints: pri

【讨论】:

  • 有趣!所以现在让我们考虑一下拥有一个受保护的类方法有什么用,以及如何解释 Katz 的评论(但对我来说那将是明天,因为我正在睡觉)。
  • comment 真的很老了(在 2007 年初 Ruby 1.9 发布之前),所以 Ruby 从那时起肯定发生了变化。我有一种感觉,他会说这没有用。你可能想看看这个blog post
猜你喜欢
  • 2013-03-24
  • 1970-01-01
  • 2013-09-22
  • 2011-04-20
  • 1970-01-01
  • 1970-01-01
  • 2016-11-12
  • 2016-11-19
  • 1970-01-01
相关资源
最近更新 更多