24 ヤン2016
Rubyプログラミング言語では、定義されたメソッドには、インスタンスメソッドとクラスメソッドの二つの亜種があります。 インスタンスメソッドは、オブジェクトが初期化されてインスタンスが作成された後に使用できます。 一方、クラスメソッドは、定義されているクラスのインスタンスを作成せずに使用できます。
Rubyメソッドは可視性が異なる場合があります。 パブリックメソッドは任意のコンテキストで使用できますが、プライベートメソッドの使用可能性はクラスとその子孫のインスタンス内で制限さ 3番目の可視性スコープであるprotectedはprivateメソッドと同様に動作しますが、protectedメソッドは同じクラスの他のインスタンスから呼び出すことができます。
簡単に復習するには、publicインスタンスメソッドとprivateインスタンスメソッドは次のようになります:
class Dog def do_trick bark end private def bark puts 'woof woof' endend
publicメソッドが呼び出されたとき:
> dog = Dog.new> dog.do_trick# => woof woof
そしてプライベートメソッド:
> dog = Dog.new> dog.bark# => NoMethodError: private method `bark' called for <Dog>
プライベートクラスメソッドはプライベートインスタンスメソッドほど一般的ではないかもしれませんが、彼らはまだ彼らの場所を持っています。 たとえば、クラスメソッドは、その関数を完了するために内部ヘルパーメソッドを必要とする場合があります。 理由が何であれ、プライベートクラスメソッドを定義することは価値がありますが、常に直感的ではありません。
この例Dog
クラスは、他のパブリッククラスメソッド内で使用されるtricks
のリストを維持する必要があります。 このリストには、Dog
クラス以外の発信者がアクセスできないようにしてください。
間違った方法
プライベートtricks
メソッドを書く際の最初のパスは次のようになります:
class Dog private def self.tricks endend
ただし、tricks
メソッドの可視性をテストする場合:
> Dog.tricks# =>
ええとああ、プライベートメソッドが呼び出されたことを示すエラーはスローされませんでした、このメソッドは完全に公開されています。
なぜ?
上記のコードがプライベートメソッドを生成しなかった理由は、Rubyのオブジェクト階層、内部クラス間の相互作用、それらのクラスのインスタンス、およびeigenclassesに関係しています。 Rubyのオブジェクトが互いにどのように動作するかについての詳細な書き込みは、以前の記事で見つけることができます。
クラス内でメソッドを定義する場合、デフォルトのコンテキストとself.
メソッド宣言によって導入されたコンテキストは区別されます。
プライベートメソッドスコープは、Dog
クラスで定義され、self
コンテキスト内で定義されたメソッド間のコンテキスト変更を処理しないため、上記の方法で
では、代替案は何ですか?
class<<self
オブジェクトのクラスメソッドを定義する別の方法の1つは、class << self
構文を使用することです。 この構文は、指定された引数の固有クラスを開きます。 この例では、Dog
クラス内のself
はDog's
固有クラスを開きます。
class Dog class << self private def tricks end endend
注意すべきことの1つは、このようなメソッドを定義するとき、宣言がdef self.tricks
から単純にdef tricks
に変更されたことです。
これで、プライベートスコープが保持され、期待される動作が達成されます:
> Dog.tricks# => NoMethodError: private method `tricks' called for Dog:Class
モジュール
rubyのモジュールは、メソッドと定数のコレクションです。 これらのコレクションは、カプセル化ツールとして、またはこの場合はpublicクラスメソッドとprivateクラスメソッドを定義する代わりに使用できます。
クラスextends
モジュールの場合、そのモジュール内のすべてのメソッドはサブジェクトクラス*のクラスメソッドになります。 このパターンは、Dog
にtricks
メソッドを定義するために使用できます。
※注: これは、”典型的な意味”で定義されたメソッドにのみ関係します。def self.
またはdef Dog.
モジュール内のメソッド定義は、extended
の場合と同じように自動的にクラスメソッドにな
class Dog module ClassMethods private def tricks end end extend ClassMethodsend> Dog.tricks# => NoMethodError: private method `tricks' called for Dog:Class
このアプローチの利点は、読みやすさです。 ClassMethods
という名前のモジュールは、Dog
クラスに含まれているため、カプセル化されており、すべての目的のクラスメソッドの明確なホームです。
private
のような可視性修飾子はそれに応じて動作し、モジュール定義の下部にある単純なextend
はそれをすべてまとめます。
private_class_method
第三のアプローチは、組み込みメソッドModule#private_class_method
を使用することです。 このメソッドの目的は、既存のクラスメソッドの可視性を変更することです。
他の二つの解とは異なり、これは間違った解とは異なるスタイルのメソッド定義を必要としません:
class Dog def self.tricks end private_class_method :tricksend> Dog.tricks# => NoMethodError: private method `tricks' called for Dog:Class
このメソッドは、クラスのメソッド定義中にインライン化することもできます:
class Dog private_class_method def self.tricks endend> Dog.tricks# => NoMethodError: private method `tricks' called for Dog:Class
単一のクラスメソッドがプライベートでなければならず、行の保存が非常に重要な場合は、このスタイルが適用される場合があります; しかし、それは確かに可読性のレベルを犠牲にします。
private_class_method
の主な強みはその明示性です。 他のアプローチとは異なり、このアプローチは特別なモジュール定義やメソッド定義コンテキストの切り替えを必要としません。
さらに読む
前述の解決策は仕事を終わらせるでしょうが、物事がなぜ彼らのやり方で働くのかについての長引く質問に答えないかもしれません。 Ruby固有クラスに関するいくつかの素晴らしい記事とRubyメソッド設計に関するMatzの考えは、Rubyのプライベートクラスメソッド定義が複雑な理由のより簡