C#やJavaなどのオブジェクト指向型言語を使った経験のある方は、「private」「public」といった、メソッドやインスタンス変数の可視性を制御するキーワードについてご存じでしょう。Rubyでは、クラス定義の中で定義したメソッドは、基本的にpublic、つまりオブジェクトの外から利用可能です。
ただし、クラス内で使うだけで、オブジェクトの外からアクセスしてほしくないメソッドを書きたい場合もあると思います。公開すべきメソッドだけを外部に公開し、それ以外は隠しておくのが基本です。
Rubyには3種類の可視性があります。以下にその一覧を示します。
ここで、コンテキストやレシーバー、selfなどの聞きなれない言葉がいくつか出てきました。今回の目標は、クラスやモジュールについて大まかな理解をすることが目的ですので、アクセス制御についての込み入った話はしません。ですので、ここでは可視性の設定方法を説明するにとどめます。次回、メソッドについて掘り下げて解説するときに、あらためて可視性について触れたいと思います。
accessibility01.rbにアクセス制御メソッドの使い方を示します。
class A def a; end def b; end private def c; end def d; end protected def e; end end
このように、privateやprotected、publicといったアクセス制御メソッドを呼び出すと、それ以降に定義する全てのメソッドにはその可視性が適用されます。この場合、メソッドaとメソッドbはpublic(デフォルトではpublicであることを思い出してください)、メソッドcとメソッドdはprivate、メソッドeにはprotectedな可視性が設定されます。
また、可視性をシンボルを使って設定することもできます。
class A def a; end def b; end def c; end def d; end def e; end public :b private :c, :d protected :e end
この場合も、メソッドaとメソッドbはpublic、メソッドcとメソッドdはprivate、メソッドeはprotectedな可視性を持つようになります。
privateに設定したメソッドは外部から呼び出せないと説明しましたが、実はRubyではその原則を簡単に破ることができます。pryを起動して、以下のように入力してみてください。
[1] pry(main)> class A [1] pry(main)* private [1] pry(main)* def private_method [1] pry(main)* puts "PRIVATE!" [1] pry(main)* end [1] pry(main)* end => :private_method [2] pry(main)> A.new.private_method NoMethodError: private method `private_method' called for #<A:0x007f85051789a0> from (pry):7:in `__pry__' [3] pry(main)> A.new.send(:private_method) PRIVATE! => nil
[1]ではクラスAを定義し、その中でprivateなメソッドprivate_methodを定義しています。このメソッドはprivateに設定されているので、[2]では「そのようなメソッドはないよ」というエラーが出ています。注目すべきは[3]の結果です。「Object#send」メソッドにメソッド名を渡すと、いとも簡単に可視性を無視して「A#private_method」メソッドを呼ぶことに成功しました。
C#やJavaなどの一般的なオブジェクト指向型言語では、基本的に可視性を簡単に破ることはできません。できたとしてもリフレクションを使うなど、簡単にはできないようになっています。
Rubyでは簡単に可視性を破れる以上、privateなどメソッドを使った可視性の制御は、クラスの設計者が「これはprivateにしたいんだよ」という意思表示程度の力しか持たないと思った方がいいでしょう。
「send」メソッドをはじめとするメタプログラミングでよく使われるメソッドは、乱用すると危険な仕組みです。しかし、それ故にプログラマーに強力な抽象化の仕組みを与えています。
これはRubyの思想の一側面で、Rubyはプログラマーを信用して全能の力を与えます。一方で、C#やJavaといった言語は「プログラマーはいつでも間違いを犯すものだ」と仮定して、言語がプログラマーの力を抑制しています。
どちらの方が優れている考え方なのかという議論は、プログラマーの間でよく酒の肴になります。一長一短があるので、結局「適材適所」ということになるのですが、筆者としては全能の力を与えてくれるRubyの方が好きです。C#も良い言語ですが、Rubyに比べると、書いてて砂を噛んでいるような気分になってしまうのです。
この辺りの話は、以降の連載のメタプログラミングの回でもう一度触れることにします。
ここまで、長々とクラスについて解説しました。RubyのクラスではC++やC#、Javaなどのオブジェクト指向型の言語と同様、クラスを定義して継承できます。ただし、C++と違ってRubyではクラスの「多重継承」を許しません。その代わりに、Rubyでは「モジュール」と「Mixin」という強力な仕組みによってコードを分割できます。
厳密には、「クラスはモジュールの一種であり、クラスはモジュールである」といえます。ただし、クラスの情報を基にオブジェクトを生成できますが、クラスではないモジュールにはオブジェクトを生成する力はありません。
ここでは基本的に、単に「モジュール」という場合はクラスでないモジュールを指すものとします。
ここからは、モジュールの定義の仕方とその利用法について解説した後、クラスとの使い分けについて触れたいと思います。
ここからは、ウサギを表すクラス「Rabbit」、アヒルを表すクラス「Duck」と、ハムスターを表すクラス「Hamster」を例に挙げ、モジュールについて理解を深めていきます。
例えば、ウサギもアヒルもハムスターも、「歩く」という点で共通しています。そこで、「歩けるもの」ということを表す「Walkable」というモジュールを定義して、Mixinしてみましょう。
module Walkable def walk puts "てちてち" end end class Rabbit include Walkable end class Duck include Walkable end class Hamster include Walkable end Rabbit.new.walk Duck.new.walk Hamster.new.walk
1行目から5行目のように、モジュールの定義はクラスの定義に似ています。「module」〜「end」間にdef式によってメソッドを定義していきます。
8行目ではRabbitクラスにWalkableモジュールをMixinしています。Mixinするためには、クラス定義の中で「include Walkable」のように書きます。こうすることで、RabbitクラスにWalkableモジュールがMixinされます。12行目も8行目と同様、こちらはDuckクラスにWalkableモジュールをMixinしています。Hamsterクラスも同様です。
19行目から21行目では、Mixinしたモジュールに定義されたメソッドがきちんと使えるかを確かめています。では、実行結果を見てみましょう。
$ ruby module01.rb てちてち てちてち てちてち
「てちてち」と3回出力されており、「rabbit.walk」「duck.walk」「hamster.walk」で、Walkableモジュールに定義されたwalkメソッドが呼び出されていることが分かります。
このように、モジュールをMixinすることで、クラス間で共通する「振る舞い(メソッド)」を部品化して再利用できます。
継承においてメソッドのオーバーライドが可能でしたが、Mixinしたモジュールのメソッドの定義をオーバーライドすることもできます。
ウサギが「てちてち」と歩くのも少しおかしい感じがしますので、Rabbitクラスではwalkメソッドをオーバーライドすることにしましょう。
module Walkable def walk puts "てちてち" end end class Rabbit include Walkable def walk puts "ぴょこぴょこ" end end class Duck include Walkable end class Hamster include Walkable end Rabbit.new.walk Duck.new.walk Hamster.new.walk
10〜12行目で、Rabbitクラスの中でwalkメソッドをオーバーライドしています。
$ ruby module02.rb ぴょこぴょこ てちてち てちてち
module02.rbの実行結果から、RabbitクラスがWalkableモジュールをMixinしているにもかかわらず、「walk」メソッドをオーバーライドしているので、「rabbit.walk」を呼び出した時だけ「ぴょこぴょこ」が出力されていることが分かります。
Copyright © ITmedia, Inc. All Rights Reserved.