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


216

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

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

7
私はプライベートクラスのメソッドを作成する方法を議論し、この記事を見て、それが良いと思った:jakeyesbeck.com/2016/01/24/ruby-private-class-methods/...
ネイサンロング

回答:


265

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

105

ExiReが書いた:

このようなルビの振る舞いは本当にイライラします。つまり、プライベートセクションself.methodに移動すると、プライベートではありません。しかし、クラス<<セルフに移動すると、突然動作します。ただ嫌です。

それを混乱させることはおそらくそうであり、それは苛立たしいことかもしれませんが、嫌なことは間違いなくそうではありません。

あなたはRubyのオブジェクトモデルを理解し、対応したらこれは完璧な理にかなっている方法は流れLOOKUPことを考慮した場合は特に、privateあるNOTアクセス/可視性修飾子が、実際のメソッド呼び出し(その受信者としてクラスで)述べたように、ここで ... Rubyには「プライベートセクション」などはありません。

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

他の主流の自己宣言型オブジェクト指向言語では、混乱が少ない構文が得られますが、Rubyのメタプログラミング機能を使用せずに、混乱して一貫性の低い(一貫性のない?)オブジェクトモデルとトレードオフします。


だから私が正しく理解すれば、ルビー自体にはアクセス修飾子キーワード(public、private、protected)がなく、アクセス修飾子メソッド(public、private、protected)があるのでしょうか?これは、Matzが適切なキーワードアクセス修飾子を実装するためにRubyバグトラッカーで取り上げるべきものですか、それともこれは予想される動作ですか?
エドワード

13
@Edwardjunichiito.blogspot.co.uk/2012/03/…そのように設計されています。なぜ「適切」なのでしょうか。
iain 2013

1
これに続いて、private_class_method :method_nameあなたがする代わりにあなたはそうすることができましたprivate_class_method def method_name...
bjt38 14

send(private_method)オブジェクトの外部からもアクセスできます。
stevenspiel 2015年

1
@ bjt38明確にするために、次のようになりますprivate_class_method def self.method_name
トム

77

デフォルトでは、すべてのクラスメソッドはパブリックです。それらをプライベートにするには、@ tjwallaceが書いたようにModule#private_class_methodを使用するか、以前とは異なる方法で定義します。

class << self

  private

  def method_name
    ...
  end
end

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


17

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

private_class_method  def self.method_name
 ....
end

5

私も、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 ...」を強制的に使用するようになります。


sendを使用する必要があるという事実は、クラスメソッドをプライベートとマークする「方法」とは関係ありません。「外部」からプライベートメソッドを呼び出すことはできません。
Pascal

4

インスタンスメソッドは、クラス定義ブロック内で定義されます。クラスメソッドは、クラスのシングルトンクラスのシングルトンメソッドとして定義され、非公式には「メタクラス」または「固有クラス」とも呼ばれます。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

- 「プライベートメッセージ」の受信機自己 -内部はclass A内部クラスオブジェクトA.自己あるclass << Aブロックが別のオブジェクト、シングルトンクラスです。

次の例は、実際には、2つの異なる受信者またはターゲットを呼び出しに使用して、privateという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の今後のすべてのインスタンスメソッドのプライベートアクセスを切り替えますが、別のクラスであるシングルトンクラスでシングルトンメソッドを宣言します。


-1

Rubyは貧弱なソリューションを提供しているようです。説明するには、プライベートクラスメソッドへのアクセスを示す簡単なC ++の例から始めます。

#include <iostream>

class C
{
    public:
        void instance_method(void)
        {
            std::cout << "instance method\n";
            class_method();  // !!! LOOK !!! no 'send' required. We can access it
                             // because 'private' allows access within the class
        }
    private:
        void static class_method(void) { std::cout << "class method\n"; }
};

int main()
{
    C c;

    c.instance_method(); // works
    // C::class_method() does not compile - it's properly private
    return 0;
}

上記を実行する

   % ./a.out
   instance method
   class method

現在、Rubyは同等のものを提供していないようです。Rubyのルールは、プライベートメソッドはレシーバーでアクセスしてはならないということだと思います。あれは、

inst.pvt_method  # FAILS
pvt_method # WORKS only within the class (good)

これはプライベートインスタンスメソッドでは問題ありませんが、プライベートクラスメソッドで問題を引き起こします。

Rubyをこのように機能させたい:

class C
    def instance_method
        STDOUT << "instance method\n"

        # Simple access to the private class method would be nice:
        class_method   # DOES NOT WORK. RUBY WON'T FIND THE METHOD
        C.class_method # DOES NOT WORK. RUBY WON'T ALLOW IT

        # ONLY THIS WORKS. While I am happy such capability exists I think
        # the way 'send' should be used is when the coder knows he/she is
        # doing a no-no.  The semantic load on the coder for this is also
        # remarkably clumsy for an elegant language like ruby.
        self.class.send(:class_method)
    end

    private_class_method def self.class_method() STDOUT << "class method\n"; end
end

しかし、悲しいかな、上記は機能しません。誰かがより良い方法を知っていますか?

メソッドの前に「送信」が表示されている場合、それはコードがAPIのデザイナーの意図に違反していることを示す明確な兆候ですが、この場合の設計では、クラスのインスタンスメソッドがプライベートクラスメソッドを呼び出すようになっています。


-14

Ruby 2.3.0以降

class Check
  def self.first_method
    second_method
  end

  private
  def self.second_method
    puts "well I executed"
  end
end

Check.first_method
#=> well I executed

private def self.second_method各メソッド表記の前にこれを試してみましたが、ルビ2.3.3では機能しませんでした。しかし、この表記は私には有効です。
Emile Vrijdags 2017年

11
これは不正解です。呼び出しCheck.second_methodも問題なく機能するため、実際にはプライベートではありません。
Deiwin 2017

1
これは機能しません。これを試してくださいprivate_class_method :second_method
キングサブリ2018年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.