web-dev-qa-db-ja.com

プライベートクラスメソッドを作成する方法は?

なぜプライベートクラスメソッドを作成するこのアプローチが機能するのか:

class Person

  def self.get_name
    persons_name
  end

  class << self

    private

    def persons_name
      "Sam"
    end
  end
end

puts "Hey, " + Person.get_name
puts "Hey, " + Person.persons_name  #=> raises "private method `persons_name' called for Person:Class (NoMethodError)"

しかし、これはそうではありません:

class Person

  def self.get_name
    persons_name
  end

  private

  def self.persons_name
    "Sam"
  end
end

puts "Hey, " + Person.get_name
puts "Hey, " + Person.persons_name
204
99miles

private は、明示的なオブジェクト(場合によってはself)でメソッドを定義している場合は機能しないようです。 private_class_method を使用して、クラスメソッドをプライベート(または説明したように)として定義できます。

class Person
  def self.get_name
    persons_name
  end

  def self.persons_name
    "Sam"
  end

  private_class_method :persons_name
end

puts "Hey, " + Person.get_name
puts "Hey, " + Person.persons_name

あるいは(Ruby 2.1+)、メソッド定義はメソッド名のシンボルを返すため、これを次のように使用することもできます。

class Person
  def self.get_name
    persons_name
  end

  private_class_method def self.persons_name
    "Sam"
  end
end

puts "Hey, " + Person.get_name
puts "Hey, " + Person.persons_name
251
tjwallace

ExiRe 書き込み:

Rubyのこのような動作は本当にイライラさせられます。プライベートセクションself.methodに移動する場合、プライベートではありません。しかし、クラス<< selfに移動すると、突然動作します。それはただ嫌です。

それを混乱させるのはおそらくイライラするかもしれませんが、嫌なことは間違いありません。

Rubyのオブジェクトモデルと対応する メソッドルックアップフロー を理解すると、特にprivateではなくアクセス/可視性修飾子ですが、実際にはメソッド呼び出し(クラスをその受信者として)前述のように ここ ...そのようなことはありませんas "プライベートセクション" Rubyの場合。

プライベートinstanceメソッドを定義するには、インスタンスのクラスでprivateを呼び出して、後で定義されるメソッドのデフォルトの可視性をprivate ...に設定します。したがって、クラスのクラスでprivateを呼び出すことにより、プライベートclassメソッドを定義することは完全に理にかなっています。そのメタクラス。

他の主流の自称OO言語は、構文がわかりにくいかもしれませんが、紛らわしく、一貫性の低い混乱とトレードオフします(矛盾する?)パワーのないオブジェクトモデルRubyのメタプログラミング機能。

103
pvandenberk

デフォルトでは、すべてのクラスメソッドはパブリックです。それらを非公開にするには、 Module#private_class_method を使用します。

class << self

  private

  def method_name
    ...
  end
end

class << selfはselfのシングルトンクラスを開き、メソッドを現在のselfオブジェクトに対して再定義できるようにします。これは、クラス/モジュール(「静的」)メソッドを定義するために使用されます。そこにのみ、プライベートメソッドを定義すると、実際にプライベートクラスメソッドが提供されます。

66
roxxypoxxy

完全を期すために、private_class_methodを別の行で宣言することも避けられます。私は個人的にこの使用法が好きではありませんが、それが存在することを知っているのは良いことです。

private_class_method  def self.method_name
 ....
end
12
Emre Basala

私も、Ruby(または少なくともそれに関する私の知識)がこの領域のマークに足りないことを発見しました。たとえば、以下は私が望むことをしますが、不器用です、

class Frob
    attr_reader :val1, :val2

    Tolerance = 2 * Float::EPSILON

    def initialize(val1, val2)
        @val2 = val1
        @val2 = val2
        ...
    end

    # Stuff that's likely to change and I don't want part
    # of a public API.  Furthermore, the method is operating
    # solely upon 'reference' and 'under_test' and will be flagged as having
    # low cohesion by quality metrics unless made a class method.
    def self.compare(reference, under_test)
        # special floating point comparison
        (reference - under_test).abs <= Tolerance
    end
    private_class_method :compare

    def ==(arg)
        self.class.send(:compare, val1, arg.val1) &&
        self.class.send(:compare, val2, arg.val2) &&
        ...
    end
end

上記のコードに関する私の問題は、Ruby構文要件とコード品質メトリックが面倒なコードのために作られていることです。コードを希望どおりに動作させ、メトリックを静めるには、compare()をクラスメソッドにする必要があります。クラスのパブリックAPIの一部にしたくないので、プライベートにする必要がありますが、「プライベート」自体は機能しません。代わりに、「private_class_method」またはそのような回避策を使用する必要があります。これにより、「==()」でテストする変数ごとに「self.class.send(:compare ...」を使用するように強制されます。

4
dinman2022

インスタンスメソッドは、クラス定義ブロック内で定義されます。クラスメソッドは、クラスのシングルトンクラスのシングルトンメソッドとして定義され、「メタクラス」または「固有クラス」とも呼ばれます。 privateはキーワードではなくメソッドです( Module#private )。

これはメソッドself#private/A#privateへの呼び出しです。これは、他の方法でトグルされるまで、今後のすべてのインスタンスメソッド定義に対してプライベートアクセスを「トグル」します。

class A
  private
    def instance_method_1; end
    def instance_method_2; end
    # .. and so forth
end

前述のように、クラスメソッドは実際にはシングルトンクラスで定義されたシングルトンメソッドです。

def A.class_method; end

または、特別な構文を使用して、Aの匿名のシングルトンクラスの定義本体を開きます。

class << A
  def class_method; end
end

「メッセージプライベート」の受信者-self-inside class AはクラスオブジェクトA. class << Aブロック内のself別のオブジェクト、シングルトンクラスです。

次の例は、実際にはprivateという2つの異なるメソッドを呼び出し、呼び出しに2つの異なる受信者またはターゲットを使用しています。最初の部分では、プライベートインスタンスメソッド(「クラスA」)を定義し、後者では、プライベートクラスメソッド(実際にはAのシングルトンクラスオブジェクトのシングルトンメソッド)を定義します。

class A
  # self is A and private call "A.private()"
  private def instance_method; end

  class << self
    # self is A's singleton class and private call "A.singleton_class.private()"
    private def class_method; end
  end
end

次に、この例を少し書き換えます。

class A
  private
    def self.class_method; end
end

[Ruby言語設計者]が犯した間違いを見ることができますか? Aのすべての今後のインスタンスメソッドに対してプライベートアクセスをオンにしますが、別のクラス、シングルトンクラスでシングルトンメソッドを宣言します。

3