Help us understand the problem. What is going on with this article?

アクセス修飾子のprivateの用途・メリット

はじめに

アクセス修飾子のprivateについての基本的な解説です。

var, let, constのように特徴やメリットがあり、開発の品質にも直接関わってくるものですのでしっかりと用途を考えて使えるように改めてまとめました。

privateはクラス外からアクセスできない

Rubyの源流であるオブジェクト指向の三大原則の一つ「カプセル化」は、あるクラスの変数やメソッドを他のオブジェクトから呼び出せないようにする考え方です。

これにより必要のない変更操作などを防ぐ安全性が担保できます。

Rubyにおいてprivateアクセス修飾子のついた(また修飾子以降に書かれた)メソッドは全てクラスの外からアクセスができなくなります。
クラスの中から呼び出される形でのみ使用することができます。

class House

  private def getAsset
# 財産を手に入れるメソッド
  end

end


外からアクセスできないメソッドを定義する理由はなんなのか?

理解していないうちはprivateなんていらないかと思う人もいますが、privateはセキュリティの観点から見て必要不可欠な機能です。

もしアクセスできる領域が限定されているべきなのに限定されていない値があったとしたら、例えば以下のようなことが想定できます。

  • 銀行口座インスタンスの残高を外部から自由に書き換えできる
  • RPGにおいてキャラクタインスタンスのステータスを外部から好き勝手に書き換えられる
  • ActionModelクラスの属性を、正規の手順を踏まずに好き勝手書き換えられる
  • あるユーザがSNSで他ユーザのパスワードをのぞき見できる

いずれも非常に危険で、アプリケーションの存続が危ぶまれるケースです。

追記

privateについて

カプセル化を実現した代表的な言語であるJava等と違い、Rubyにおけるアクセス修飾子は以下のような挙動をします。

Rubyリファレンスマニュアルによると、privateは以下のようにあります。

「private に設定されたメソッドは関数形式でしか呼び出せません。」

つまりインスタンス.privateメソッドの形式では呼ぶことができないということですね。

このときメソッドを呼び出されるインスタンスのことを、「“メソッドを実行しろ“というメッセージを受け取っている対象」と解釈して「レシーバ」と呼んだりします。

クラス内で直接privateメソッドを呼び出すことができるけれども、レシーバ形式での呼び出しが不可能になります。

class A

  def call_private
    private_method    # 関数形式での呼び出し(できる)
  end

  def call_private_with_reciever
    self.private_method    # レシーバ(self)形式での呼び出し(できない)
  end

  private

    def private_method
       p "只今privateメソッドテスト中!"
    end

end

a = A.new
a.call_private #呼び出せます
a.call_private_with_reciever #エラーになります
また、呼び出そうと思えばクラスの外からもsendメソッドでprivateなメソッドを呼び出せてしまいます。
よって、Rubyのアクセス修飾子は厳密なカプセル化を実現する目的とはまた違うみたいですね。

a.send(:private_method) # 呼び出せます

※やろうと思えばKernel#exitを呼び出して、アプリケーションを強制終了させることも外部から可能になってしまうので、外部から引数として変数名を入力させるアプリケーションは大変危険ですので実装すべきではないです。

protectedについて

また、protectedについてはリファレンスによるとこうあります。

「protected に設定されたメソッドは、そのメソッドを持つオブジェクトが selfであるコンテキスト(メソッド定義式やinstance_eval)でのみ呼び出せます。」

つまり、同一クラス(および継承先クラス)からならレシーバ形式self.protectedメソッドでも呼び出せるということですね。

class Sample
  def == other #==の処理を独自に更新しようとしています。Rubyでは==をはじめ全ての演算子が関数オブジェクトとして扱えるので、このような更新が可能です。
    if self.class == other.class
      internal == other.internal
    else
      super
    end
  end
  protected
  def internal; :sample; end
end

Sample.new == Sample.new #=> true

まとめ

privateの重要性は現実世界では大切なものを守るとき、例えば家に置いてある財産を泥棒に取られないようにするために「家の出入り口に鍵をかける」ことと考え方は同じです。

大切なものに対するアクセスは得てして不自由である方が扱いやすい」のです。

参考

この記事は「CodeShip」内での実際の質疑応答や指導・アドバイスの一部を基に作成しています。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした