Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

Rubyのprivateメソッドとprotectedメソッドを徹底解説!メソッドの可視性をマスターしよう

Rubyにおける「プライベートメソッド」とは、そのメソッドが定義されたクラスの内部からしか呼び出せないメソッドのことです。

privateやprotectedといった可視性(visibility)を適切に設定することで、クラス外部からのアクセスを制御し、より安全で保守しやすいコードを書くことができます。

Rubyの3つのメソッド可視性

Rubyのメソッドには、以下の3種類の可視性があります。

  • private … クラス内部からのみ呼び出せる
  • public … デフォルト。誰でも呼び出せる
  • protected … クラス内部+同クラスのインスタンス経由で呼び出せる

デフォルトでは、すべてのメソッドはpublicになります。つまり、何も指定しなければ誰でもそのメソッドを使える状態です。

しかし、privateprotectedを指定することで、この挙動を変更できます。

なぜ可視性の制御が重要なのか?

publicメソッドを減らすことで、クラス内部を自由に変更できる余地が広がるからです。

具体例を挙げてみましょう。あなたが職場で複数のプロジェクトから利用されるライブラリを開発しているとします。各プロジェクトは、あなたが作成したクラスのメソッドを呼び出して使うことになります。

ここで新しいバージョンをリリースする際に、あるpublicメソッドの名前を変更したらどうなるでしょうか?

答えは簡単です。そのメソッドを利用しているすべてのプロジェクトでエラーが発生してしまいます。

逆に言えば、publicメソッドの数を最小限に抑えておけば、クラスの内部実装は自由にリファクタリングできるのです。これがメソッドの可視性を意識する最大の理由です。

それでは、実際のコード例を見ていきましょう。

Rubyのprivateメソッドを理解する

「private method called」というエラーメッセージを見たことはありませんか?

self.puts 123

# NoMethodError: private method `puts' called

これは、privateメソッドを誤った形式で呼び出そうとしたときに発生するエラーです。

privateメソッドはレシーバを明示せず、単体で呼び出す必要があります。

puts 123

同じメソッドでも、このように呼び出せば正常に動作します。

privateメソッドは常にselfのコンテキスト内で呼び出されるため、次のような場合にのみ使用できます。

  • 同じクラス内の他のメソッドから
  • 親クラスから継承したメソッドから
  • モジュールからincludeしたメソッドから

つまり、privateメソッドは定義されたクラスの外部からは呼び出せません。外部から呼び出すには「明示的なレシーバ」が必要になるためです。

ただし、例外的な手段として、sendメソッドを使えばこのルールを回避できます。

send(:puts, "apple")

では、privateメソッドはどうやって定義するのでしょうか?

def bacon
  "private bacon"
end

private :bacon

実はprivateという言葉自体はキーワードではなく、Kernelモジュールで定義されたメソッドなのです。

privateメソッドはどこに書くべき?

1つのクラスが複数のprivateメソッドを持つのはごく普通のことです。では、これらのメソッドはどこに配置すればよいのでしょうか?

おすすめの書き方はこちらです。

class Food
  def public_method
  end

  private

  def bacon
  end

  def orange
  end

  def coconut
  end
end

private以降に定義されたすべてのインスタンスメソッドは、自動的にprivateメソッドになります。

クラスメソッドをprivateにしたい場合は、private_class_method :メソッド名を使用します。

実務では「publicメソッドを先に定義し、private以下にまとめてprivateメソッドを配置する」というパターンが一般的です。コードの可読性も大きく向上します。

publicメソッドとは

publicはRubyにおけるデフォルトのメソッド可視性です。

def orange
  "Vitamin C"
end

orangeメソッドを持つオブジェクトfoodがあれば、次のように呼び出せます。

food.orange

また、一度privateやprotectedに変更したメソッドも、再度publicに戻すことができます。

public :orange

protectedメソッドとは何か?

protectedメソッドは、privateメソッドほど頻繁に使われるものではありません。

protectedはprivateと似ていますが、「同じクラス(またはサブクラス)の他のインスタンスに対して、レシーバを明示して呼び出せる」という点が異なります。

もし上記の例をprivateで試すとエラーになります

class Food
  def initialize(name)
    @name = name
  end

  def ==(other)
    name == other.name
  end

  protected

  attr_reader :name
end

food = Food.new("chocolate")

puts food == food

nameがprivateだと、other.nameという形で呼び出せないためエラーが発生します。

しかしprotectedであれば、このコードは正常に動作するのです。

privateとprotectedの違いとは?

まとめると、protectedはメソッドをクラス外から隠しつつ、同一クラスのインスタンスに対してはレシーバ付きで呼び出せるという違いがあります。

  • privateの場合:name のみ呼び出し可能
  • protectedの場合:object.name の形式でも呼び出し可能

では、protectedはいつ使うべきなのでしょうか?

基本的には、等価比較(==)メソッドのように、同クラスの別インスタンスの内部状態を比較する必要がある、非常に限られたケースだけです。

Rubyの公式ドキュメントでも、可能な限りprivateを使うことが推奨されています。

さらに、ドキュメントにはこんな注記もあります。

「protectedメソッドはインラインキャッシュを利用できないため、遅くなる。」

気になったので、実際にベンチマークを実行してみました。

public:    2813891.9 i/s
private:   2699273.8 i/s
protected: 2572122.0 i/s

結果として、protectedはpublicと比べて約8.5%性能が劣ることがわかりました。パフォーマンス面でも、privateを優先する理由があると言えます。

動画チュートリアル

記事だけでなく、動画でも学習したい方のために解説動画をご用意しています。

まとめ

今回はRubyのメソッド可視性について、public・private・protectedの3種類を学びました。

重要なポイントをおさらいしましょう。

  • private:クラス内部からのみ、レシーバなしで呼び出せる
  • public:デフォルト。どこからでも呼び出せる
  • protected:同クラスのインスタンスに対してはレシーバ付きで呼び出せるが、やや遅い

そして忘れてはならないのが、privateprotectedはRubyのキーワードではなくModuleクラスで定義されたメソッドだという点です。

publicメソッドを絞り込むことでクラスの保守性は大きく向上します。ぜひ日々のコーディングに活かしてください。

この記事が役に立ったら、ぜひ周りの方にもシェアしてくださいね。

最後まで読んでいただきありがとうございました 🙂

  1. Rubyのメソッド入門:defキーワードで独自のメソッドを定義する方法

    Rubyにおける「メソッド」とは、特定の目的のためにひとまとめにされた、1行以上のRubyコードのことです。 このコードのまとまりには名前が付けられており、一度定義してしまえば、同じコードを何度も書いたりコピー&ペーストしたりすることなく、必要なときにいつでも再利用できます。 メソッドの主な役割 メソッドの目的は、大きく分けて以下のようなものがあります。 情報を取得する オブジェクトを変更・生成する データをフィルタリング・整形する 具体例 例1: Arrayオブジェクトに対するsizeメソッドは、配列内の要素数を返します(情報の取得)。 例2: popメソッドは、配列から最後の要素を取り

  2. Rubyのfreezeメソッド完全解説 – オブジェクトの可変性と不変性を理解しよう

    オブジェクトが「変更可能(ミュータブル)」であるとは、どういう意味なのでしょうか? 難しい言葉に構える必要はありません。「可変性(ミュータビリティ)」とは、単純に「オブジェクトの内部状態を後から変更できる」という意味です。これはすべてのオブジェクトのデフォルトの挙動であり、freeze(凍結)されたオブジェクトや、言語側で特別扱いされている一部のオブジェクトだけが例外となります。 つまり、Rubyのすべてのオブジェクトが変更可能というわけではないのです。 なぜ数値やシンボルは変更できないのか? たとえば、整数・シンボル、さらにはtrueやfalse(これらもすべてオブジェクトです)が変化するの