Rubyプライベートクラスメソッド

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クラス内のselfDog'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モジュールの場合、そのモジュール内のすべてのメソッドはサブジェクトクラス*のクラスメソッドになります。 このパターンは、Dogtricksメソッドを定義するために使用できます。

※注: これは、”典型的な意味”で定義されたメソッドにのみ関係します。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のプライベートクラスメソッド定義が複雑な理由のより簡

You might also like

コメントを残す

メールアドレスが公開されることはありません。