LoginSignup
18
4

More than 3 years have passed since last update.

Rubyのprivate修飾子からアクセス修飾子を考える

Last updated at Posted at 2019-05-07

Rubyのprivateの注意点(特に他の言語とどこが違うのか)については各所の記事で説明がありますが、その背景については断片的にしか情報がないと思ったので、少しまとめてみようと思います。

以下の記事の補足のような内容です。

Ruby の private と protected 。歴史と使い分け
https://qiita.com/tbpgr/items/6f1c0c7b77218f74c63e

結論だけ先に書くと

次の二点に要約できます。

  1. アクセス修飾子は、外部から呼べるかどうかを「明示」「記述」するための機能で(も)ある
  2. 「外部から呼べる」というのは、設計上「呼ばれることを想定している」ということを意味し、「外部から呼べない」というのは、「呼ばれることを想定していない」という意味であり、実際に「呼べる」かどうかの問題ではない

一体何を言っているんだと思われた方は、以下をご覧ください。

きっかけ

いまRubyとRailsを学んでいます。privateprotectedのあたりの使い分けに戸惑うのはもはや定番なのだと思いますが、案の定もやもやするものがあったので、調べてみることにしました。

まず気になったのが、次の記事での記述です。

Rubyのprivateを考える - sometimes I laugh
https://sil.hatenablog.com/entry/rethinking-ruby-private-access-modifier

でも、これとはまた違った話で、そもそもprivateはアクセスコントロールとして設計されたものではないので、そのように使うべきではない。という意見(そもそもprivate不要論)もあって、これはこれで面白い。

Rubyのprivateは「アクセスコントロールとして設計されたものではない」とのことです。そしてそれを示す発言がこちら。

アクセス修飾子は「アクセス制御」のための機能だと思っていたので、この説明が何を意味するのかわかりませんでした。アクセス制御のための機能でないなら、一体なんのためにあるんでしょう。

そもそもprivateを使うと何ができるのか

ひとまず、Rubyにおけるprivateがどのような機能なのか振り返ってみます。

クラス/メソッドの定義 (Ruby 2.1.0)
https://docs.ruby-lang.org/ja/2.1.0/doc/spec=2fdef.html#limit

この節のタイトルが「呼び出し制限」になっているのがまたややこしいのですが、今回は見なかったことにしましょう。

次のように説明されています。

public に設定されたメソッドは制限なしに呼び出せます。
private に設定されたメソッドは関数形式でしか呼び出せません。

ただし、次の二点には注意が必要です。

  • 派生クラスからでも呼ぶことができる
  • sendを使えば外部からでも呼べる

2番目については、次の例が明快です。

Ruby の private メソッドを外部から呼び出す - Secret Garden(Instrumental)
http://secret-garden.hatenablog.com/entry/2015/07/21/212314

class X
    private
    def private_method
        "private_method"
    end
end

x = X.new
x.send(:private_method)
# => "private_method"

こういうわけなので、「アクセスコントロール」(外部から使うことが不可能なようにする)には不向きな機能なのではないか、というのが前掲のツイートの背景です。

Rubyのアクセス修飾子の源流

最初に紹介したQiita記事で触れらている以下の記事をよく読んでみると、次のツイートが紹介されています。

JavaやC#の常識が通用しないRubyのprivateメソッド - give IT a try
https://blog.jnito.com/entry/20120315/1331754912

Rubyのprivateが考案された当時は、現在では一般的になっている(と言えるであろう)C++やJava系のprivateの意味合いはまだ浸透していなかったようです。ということは、意図的にややこしくしたわけではなさそうです。
当時はJavaもRubyも黎明期だったので、どちらが正しいという話でもなかったんですね。(Ruby以外の言語の経験が少ない場合、「Rubyのprivate修飾子がなぜわかりにくいと言われるのだろうか」と疑問に思うこともあるようですが、どちらのprivateの用法にもそれぞれ考案当時からの正当性があるわけなので、当然といえば当然かもしれません。)

それから、次のツイートが謎を解く鍵になります。

ここでSmalltalkの話が出ますが、よく知らないので少し調べてみます。次の回答で雰囲気が少しわかりました。

coding style - Smalltalk public methods vs private/protected methods - Stack Overflow
https://stackoverflow.com/questions/7399340/smalltalk-public-methods-vs-private-protected-methods

Indeed, the Smalltalk way is to put private methods in the 'private' category. This indicates that you shouldn't use these methods, but of course doesn't enforce this.

Smalltalkにおいては、private categoryなので呼ばないでくださいね、と明示することはできても、それに強制力はないという言語仕様になっているようです。そこで、Rubyはそれよりも強制力を強めて、呼ぶことを実際に不可能にした、というのがprivateができた背景ということですね。

ここで、これを調べているときに見つけた別のサイトの説明にも注目します。

アクセス修飾子
http://bliki-ja.github.io/AccessModifier/

アクセス制御はアクセスを制御するわけではない
privateなフィールドは他のどのクラスもアクセスできないということを意味する……’'’わけない!’'’ほとんどどの言語でも、アクセス制御の仕組みを壊すことは可能なのです。たいていは、リフレクションを使った方法を用います。なぜなら、デバッガやその他システムツールがprivateなデータをみる必要があるためです。そのため、たいていリフレクションインターフェイスによってそれが可能となります。
(中略)
アクセス制御の目的は、アクセスを防ぐことではなく、むしろクラスがあることを秘密にしておきたがっているという合図を送ることなのです。アクセス修飾子を使うということは、プログラミングにおける多くのことがそうであるように、本来はコミュニケーションに関するものなのです。

ここにも「アクセスを防ぐことではなく」というくだりがあります。「アクセス制御」と呼んでいるのにも関わらず、「制御」したいわけではないというのはどういうことなんでしょうか。「本来はコミュニケーションに関するものなのです」という説明もなかなか意味深です。

アクセス修飾子の役割

原点に立ち返り、「アクセス修飾子」「アクセス制御」について調べてみます。
次の大学の先生の説明と思しきページの説明がわかりやすいです。

オブジェクト指向超入門
http://www.edu.tuis.ac.jp/~mackin/software/2005/objectoriented.html

このようにメンバ(フィールドとメソッド)には、外部から使える(見える)、使えない(見えない) という制御が可能であり、定義の頭に"private"や"public"といったアクセス修飾子をつけることで行われる。
オブジェクト指向では、オブジェクトの中身で外に見せるべきでないところを隠し、 見せるべきところだけを公開する方法を取る。 これをカプセル化といい、見せるべきでないところを隠すことを情報隠蔽という。 これらを記述するのがアクセス修飾子である。

「外に見せるべきでないところを隠し、 見せるべきところだけを公開する方法を取る」ため、「これらを記述するのがアクセス修飾子」と説明しています。
ここで「制限する」「制御する」とか「制約する」とかではなくて、「記述する」と言っているのがポイントかもしれません。すなわち、Smalltalkの場合と同じく、外部から使っていいのかどうかを明示するために用意された機能である、と読むことができます。

もう一つ、辞書的なものも見てみましょう。

What is Access Modifiers? - Definition from Techopedia
https://www.techopedia.com/definition/23/access-modifiers

Access modifiers are keywords used to specify the accessibility of a class (or type) and its members. These modifiers can be used from code inside or outside the current application.
(中略)
The purpose of using access modifiers is to implement encapsulation, which separates the interface of a type from its implementation. With this, the following benefits can be derived:

"accessibility"を指定するために使うのがアクセス修飾子ということになります。accessibilityは、英単語としては次のように説明されています。

《コ》〔ウェブサイト・データベースなどへの〕アクセス可能性、アクセスのしやすさ

さらに、次の文を読むと、"encapsulation"を実現する云々の話があります。ここで、もしやキーワードはこのencapsulationなのでは、ということに気づきます。

オブジェクト指向の基本、「カプセル化」「隠蔽化」

「カプセル化」の一般的な説明をどこで見つけるかという問題もあるのですが、とりあえず辞書を見てみます。

カプセル化(かぷせるか)とは - コトバンク
https://kotobank.jp/word/%E3%82%AB%E3%83%97%E3%82%BB%E3%83%AB%E5%8C%96-2428
(以下孫引用)

大辞林 第三版
コンピューターで、ある複合的なデータが存在するときに、データ構造の内部の情報を外部から直接参照できないようにし、代わりにデータ操作のためのインターフェースを外部に提供すること。 → オブジェクト指向プログラミング

なぜ直接参照できないようにするのかという部分は、次のように説明されています。

世界大百科事典内のカプセル化の言及
…これによって,外部からの不用意なアクセスによってモデュールが扱うデータの一貫性が損なわれないようにできる。この機能を情報隠蔽やカプセル化などと呼ぶ。

ここまでくると、privateうんぬんの話ではなく、オブジェクト指向ってなんだったっけ?という話になります。

オブジェクト指向は、オブジェクトとオブジェクトの間でメッセージをやりとりして、その作用の連鎖によって処理を進めていくという設計手法です。その中で、オブジェクトが別のオブジェクトにメッセージを送るためには、そのための公開されたインタフェースが必要になります。これがpublicなインタフェースということになります。

他方で、オブジェクトの内部でどのような値を持っているのかや、どのような処理をしているのかというのは、見えなくてもいい部分になります。じゃあ見えてもいいのではないか、というと、そういうわけではありません。もし内部の値が操作されたり、外部から呼ばれることが想定されていないメソッドが呼ばれたりすると、設計者の意図しない動作を引き起こす可能性があります。また、外部から利用されることが想定されていないメンバは、動作が変更されたり削除されたりする可能性もあり、潜在的なバグの原因になります。そういうわけなので、オブジェクトの内部で起きていることというのは、見えなくていいし、見えないほうがいいと言うことができます。

ということで、オブジェクトの内部だけで扱うべきこと(その系の内側だけの事柄として扱うべきこと)は、そのオブジェクトの内部に閉じ込めておき、外部からは見えないようにしましょう、というのがカプセル化(隠蔽化)です。このカプセル化を実現するために、外部からの利用を想定しているものか、そうでないのかというのを明示する必要があり、そのための機能がアクセス修飾子である、というわけです。

結論

結局オブジェクト指向の復習をしただけじゃないか、という気もします。ここで、先述したポイントを再掲します。

  1. アクセス修飾子は、外部から呼べるかどうかを「明示」「記述」するための機能で(も)ある
  2. 「外部から呼べる」というのは、設計上「呼ばれることを想定している」ということを意味し、「外部から呼べない」というのは、「呼ばれることを想定していない」という意味であり、実際に「呼べる」かどうかの問題ではない

アクセス修飾子は、オブジェクト指向の肝でもあるカプセル化を実現するための手段として(それを目的として)用意されたものであって、privateなものを外部から使おうとするとコンパイルエラーになったり、実行時エラーになったりするというのは(実際に使えなくするという意味での「アクセス制御」は)、あくまでそのおまけ(補助機能)にすぎない、という発想をしたほうがよさそうです。

これはもしかしたら、"accessibility"の"-ibility"の部分、「呼べる」の「べる」の部分をどのように捉えるのか、という問題と言えるかもしれません。
「1トンの鉄塊を素手で持ち上げることはできない」というのと、「禁煙車で喫煙をすることはできない」というのでは、「できない」の意味合いが変わってくるわけですが、これと同じです。実際に呼べなくなるという結果中心の見方をとると、「制限できない制限機能」のような印象を受けるのですが、隠蔽化のための記述の一つであるという目的中心の見方をとると、きちんと意味のある機能なんだな、と考えることができると思います。

18
4
0

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
18
4