LoginSignup
14
10

More than 5 years have passed since last update.

protected, privateをわかりやすく、かつ楽に

Last updated at Posted at 2014-05-06

個人的に見ることの多いRubyのソースに、次のようなものが多いです。

def methodA
  ...
end

protected

def methodB
  ...
end

private

def methodC
  ...
end

確かに僕も書くならまあこうするっちゃします。でもやっぱりprotectedなメソッドが増えて、privateなメソッドも増えたりすれば今見ているメソッドのアクセス権もわからなければ、privateなメソッドを追加したいときにどこに追加したらいいのかわからないこともあります。

そしてこの問題の解決策の1つとしてネット上でよく見るものに次のようなものがあります。

def methodA
  ...
end

protected
  def methodB
    ...
  end

private
  def methodC
    ...
  end

確かにインデントのレベルを変えれば少なくともpublicなメソッドでないことが分かります。もしprivateをすべてprotectedに置き換えてしまえ!なんてプロジェクトだったらもうすぐに判別がつきますね。

ただこの場合、クラス定義全体を見るとこのようになります。

class Klass
  def methodA
    ...
  end

  protected
    def methodB
      ...
    end

  private
    def methodC
      ...
    end
end

そう、最後の連続のendのインデントが奇妙になるのです。まぁこの辺は慣れればなんてことないのかもしれません…(ちなみにprotected, privateの次の行からbegin...endで囲んでendのインデントを合わせる方法もありました)。

ただ、個人的に「おややっ」となるのがクラスメソッドですね。 privateという記述より後に書こうがprivateメソッドにならない のです。これ知らなかったときはハマりました。

どうすればいいかと言えば

def self.class_methodA
  ...
end
private_class_method :class_methodA

とするか

class << self # この行は instance_eval do でも(ほぼ?)同じことが可能
  private
  def class_methodA
    ...
  end
end

# ちなみにこの手法の場合はprotectedなメソッドもprivateと同様に定義可能
# 上の手法だとprivateなメソッドのみ定義可能

とするか、だと思います。

インスタンスメソッド定義とまとめて書けば、
(個人的にメソッドはいつも、アクセス権ごとに分けてクラスメソッドから順に定義してるのでそれに従って書きます)

class Klass
  def self.class_methodA
    ...
  end

  def methodA
    ...
  end

  protected

  def methodB
    ...
  end

  private

  def self.class_methodB
    ...
  end
  private_class_method :class_methodB

  def methodC
    ...
  end
end

もしくは

class Klass
  # もはやアクセス権ごとに分けるルールが適用できない(今回の件では全く重要ではないけど)
  class << self
    def class_methodA
      ...
    end

    private

    def class_methodB
      ...
    end
  end


  def methodA
    ...
  end

  protected

  def methodB
    ...
  end

  private

  def methodC
    ...
  end
end

という風になります(インデントレベルを変えるパターンは省略します)。
非常に面倒くさいですね!

僕の理想は次のコードです。

class Klass
  def self.class_methodA
    ...
  end

  def methodA
  end

  protected do
    def methodB
      ...
    end
  end

  private do
    def self.methodB
      ...
    end

    def methodC
      ...
    end
  end
end

ということで理想通りに書けるようなモジュールを書きました(Gist)。テストは書いてませんが…。
クラス内でincludeすれば上記のように書けます。

さて、最後ですが皆さんこの辺どうしてますか。むしろテストまで書かれた同じようなモジュールやライブラリ、もしくは別の解決策となるモジュールやライブラリがあるのでしょうか。何かいい案があればご教授下さい。

14
10
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
10