まさ@ブログ書き込み中

まさ@ブログ書き込み中

まさの旅、英語、プログラミング、プライベートについて、色々記録しています。

パーフェクトRuby4章「クラスとモジュール」のまとめ

 

こんばんは、まさです。

 

約束通りブログ書くよし。

 

今日はパーフェクトRubyの4章「クラスとモジュール」について。

改訂2版 パーフェクトRuby

改訂2版 パーフェクトRuby

 

 

 

クラス

まずはクラスについて。僕が「おっ」思ったことについてざっくりまとめると

  • MyClass.newしたときにインスタンスメソッドとしてinitializeが定義されていればそれを呼び出します。
  • class クラス名 ... end の中でインスタンスメソッドを定義しますが、あるメソッドから同じインスタンスのメソッドを呼び出す際にはレシーバを省略できます
  • メソッドの呼び出し制限は、public, private, protectedとあって、privateはレシーバを省略する形でしか呼び出せず、protectedはそのメソッドが定義されたのと同じクラス、またはサブクラスのインスタンスからしか呼び出せない
  • 特異メソッドとは、あるオブジェクト固有のメソッドのこと

 

少し補足していきます。

同じインスタンスのメソッドを呼び出す際にはレシーバを省略できる

これみたら一発でわかると思います。

class Masa

   def call_a

       puts "I am Masa A!"

   end

 

   def call_b

       call_a

   end

end

 

このコードで、masa = Masa.newのあとにmasa.call_bでcall_bメソッドを呼び出した時、レシーバはmasaですが、call_bメソッド内で呼び出されるcall_aにはレシーバがありませんね。

 

つまり、同じインスタンス(ここではmasa)のメソッドを呼び出す際にはレシーバを省略できるわけです。もちろん、最初に呼び出されるべきメソッドcall_bにはインスタンスが必要ですが。

 

これは当たり前に見えますし、僕も無意識にやってました。

 

ちなみに

しかーし!それはつまり、以下のようなこともできるということなのです。

class Masa

   def self.pair

       [new, new]

   end

end

 

これでMasa.pairをすると2つのMasaオブジェクトを返すことになります。

先ほどの説明では「同じインスタンスのメソッドを呼びだす際には・・・」と言いましたが、どうやらクラスメソッドにもレシーバの省略は有効だそうな。 

 

ここで「[new,new]ってなんぞや」って思った僕と同じビギナーの皆さん。これはMasa.newのレシーバであるMasaが省略されたものだと思ったらわかるのではないでしょうか。つまり[Masa.new, Masa.new]となるわけです。

 

メソッドの呼び出し制限

ここは何度読んでもわかりづらかった。publicはわかりやすいので説明は省略するとして、曲者はprivateとprotected。

 

説明を始める前に「そうそう!この二つの違いを知りたかったんだよ!」って思っている皆さんのやる気を削ぐ二文を本書から引用させて頂きます。

protectedのような呼び出し制限が必要となる場面はほとんどありません。単にインスタンスの外からの呼び出しを制限したい場合にはprivateを用いましょう。 

 

 

・・・は?

 

 

って感じですよね。何がタチ悪いかって、この二文が出るのは、privateとprotectedについてめっちゃ説明された。しかも、めくった次のページにある。そんなこと言われるとは知る由もなく、ずっと次のページに進まずうーん、うーんと言って考えてた自分が哀れに思えたのは内緒。

 

まあ、でも、僕には関係ありません。説明を始めます。

privateメソッド

privateメソッドの宣言はprivate :method_dayoのようにできます。

privateメソッドはレシーバと共に呼び出すことは出来ません。つまり、あるクラスの定義式の中で他のメソッドとの連鎖の中で使われたりするものなのです。

 

protectedメソッド

protectedメソッドの宣言はprivateメソッドと同じようにprotected :protected_dayoのようにできます。

protectedメソッドは

  1. そのメソッドが定義された同じクラスのインスタンス
  2. そのサブクラスのインスタンス

からのみ呼び出されます。

 

privateメソッドとの違いは、レシーバがあっても良いこと。publicメソッドとの違いは、(サブクラスでなければ)クラス内でなければ使えないこと。

 

例を見てみましょう。

 class Masa

   def process(other)
       other.protected_process

   end

 

   def protected_process

       puts "Called"

   end

   protected :protected_process

end

 

このクラス定義式で、

masa = Masa.new 

masa.process(masa.new)

とすると「Called」と出力されます。

ちなみに、masa.protected_processとするとエラーが返ってきます。

 

赤字のポイントに注目してもらいたいのですが、

privateメソッドはレシーバを取らなかったのに対して、protectedメソッドはそのメソッドが定義されたクラス内であればレシーバを取ることができます。だから、上の実行例で引数として渡されたmasa.newはprotected_processメソッドを呼び出すことができて、masa.protected_processは呼び出すことができませんでした。

 

繰り返しになりますが、protectedメソッドはサブクラスのインスタンスであればそんなこと気にせず自由に呼び出せます。

 

言い換えると、

  • public(公開されていて呼び出しに関しては気にしなくて良い)
  • private(秘密なのでクラス外には一切出ない)
  • protected(privateのようにある程度守られているけどサブクラスのインスタンスからは全然呼び出せる)

というわけですな。

 

モジュール

次はモジュールについてまとめていきます。実は僕はモジュールを使ったことがありませんでした。

 

モジュールとはクラスのように任意の式やメソッド定義を記述できますが、

という点で大きく異なります。

 

また、モジュールの用途としては

  • 名前空間を作る
  • モジュールのメソッドをあるクラスのインスタンスメソッドとして取り込む
  • モジュールのメソッドをあるオブジェクトの特異メソッド(クラスメソッド)として取り込む
  • 特異メソッドやモジュール関数を定義して使う

というものがあるそうな。少し詳しくみていきましょう。

 

特異メソッドをつくる

特異メソッドとは文字通り、ある特別なオブジェクトなどによってのみ利用されるメソッドですが、モジュールの特異メソッドは

module Masa

     def self.hello

         puts "hello!"

     end

end 

 

のようにしてつくられ、Masa.helloとすると「hello!」が出力されます

 

モジュール関数

モジュール関数とは「関数」という名の通り、あるインプットに対して決まったルールでアウトプットを返すというだけのものです。

 

なので、モジュールをレシーバとするか、レシーバ無しで呼び出します。

 

その意味で、privateなインスタンスメソッドであると同時に、モジュールの特異メソッドでもあると『パーフェクトRuby』では言われています。

 

あるクラスのインスタンスメソッドとして取り込む

これがもっともイメージしやすいモジュールの役割ではないでしょうか。

モジュールの中に定義したメソッドなどは、あるクラスの定義式内にinclude <モジュール名>と書くことで、そのクラスやクラスのインスタンスが使えるようになります。

 

ちなみにクラスに取り込んだモジュールの中のselfの意味は、そのモジュールを取り込んだクラス自身になります。直感的にわかりやすいですね。

 

あるオブジェクトの特異メソッド(クラスメソッド)として取り込む

モジュールは、自身の特異メソッドや、関数、クラスのインスタンスメソッドとして取り込む以外にも、あるオブジェクトの特異メソッド(クラスメソッド)として取り込むことができます。

 

取り込みたいオブジェクトにextendと書く(例:masa.extend MasaModule)ことによって、そのオブジェクトにだけ特定のふるまいを与えることができます。

 

本書ではそのことを以下のように説明していました。

例えば、Humanオブジェクトに教師としての振る舞いをさせたい場合にはTeacherモジュールをextendするという具合です。

 

 

おいおい、わかりやす過ぎだろ。

 

 

また、クラスもまたオブジェクトの一つなので、クラス定義内でモジュールをextendすると、メソッドをクラスメソッドとして取り込むことができます。

 

 

今日はここまで。

これで大体クラス・モジュールに関する理解は深まったのではないでしょうか。

では、おやすみなさい。