環境
Ubuntu 20.4
Ruby 2.6
はじめに
Rubyには特異メソッドといったものがあります。特異メソッドとは、クラスの内部ではない場所に独立したメソッドを書き、あたかも、そのクラスの内部に実在しているかのように扱うメソッドのことです。また、拡張的な使い方として、superメソッドを使うことで、クラスの内部に実在するメソッドに対し、機能を追加することもできます。
自身の経験上、特異メソッドを使わざるを得ない状況に陥ったことは、一度もありません。殆ど、個人でしか開発を行った経験がないため、自分で作ったクラスにメソッドを追加したい場合は、実際に、クラスに追加すれば済むためだけの話だからです。
勉強のためだけの目的でむりやり特異メソッドを使って、メソッドを追加してもいいのだが、後々の可読性、メンテナンス性のことを考えると、あまりしたくはありません。他人が見たとき、「ん?、この人は、何がやりたいのだろう?」となってしまう可能性が非常に高いです。
恐らくではあるが、特異メソッドを使うケースとして、他人が作成したクラスに対し、「このクラスにメソッドを追加したい!」といった衝動に駆られるが、安易にカスタマイズができない事情がある場合は、特異メソッドを使って追加することになるかと思います。
そもそもメソッドは、クラスを作った人のルールに従って、ルール通りの使い方をするのではなく、自由に使っていいんだよというメッセージが、特異メソッドにはあるのかも知れません。自由である反面、やたら多用すると、それはそれで辟易しそうな気がするので、他人には使って欲しくない機能ではあります。あくまでも個人的な感想です。
動作確認
特異メソッドの動きについて動作確認をしてみました。
Testクラスにtest1メソッドがあります。test2メソッドは含まれていません。しかし、Testクラスをインスタンス化して作ったts変数に、test2メソッドを含めることができます。test2メソッドが独立して動いているのはなく、ts変数のメソッドとして動作しているのです。これが特異メソッドです。
class Test
def test1
puts "OK1"
end
end
ts = Test.new
ts.test1
def ts.test2
puts "OK2"
end
ts.test2
動作結果です。
OK1
OK2
ts.test2の代わりに、 Test.new.test2としてみました。こういう書き方もできるだろうかと思って書いてみたが、この書き方はできません。エラーになります。
def Test.new.test2
puts "OK2"
end
superの使い方
Testクラスの中にあるメソッドと同じ名前のメソッドを、特異メソッドとして記述することもできます。そうすれば、既存のメソッドが上書きされます。
class Test
def test1
puts "OK1"
end
end
ts = Test.new
ts.test1
def ts.test1
puts "OK2"
end
ts.test1
動作結果です。
OK1
OK2
特異メソッド内にsuperを1行追加すると、superの個所で、Testクラスのtest1メソッドが呼び出されます。
class Test
def test1
puts "OK1"
end
end
ts = Test.new
ts.test1
def ts.test1
super
puts "OK2"
end
ts.test1
動作結果です。
OK1
OK1
OK2
superの位置を変えると、Testクラスのtestメソッドが呼び出されるタイミングも変わります。Testクラスのtest1メソッドが実行されて、その後に、特異メソッドであるtest1メソッドが実行されるといった流れとなります。
class Test
def test1
puts "OK1"
end
end
ts = Test.new
ts.test1
def ts.test1
puts "OK2"
super
end
ts.test1
動作結果です。
OK1
OK2
OK1