メタプログラミングRuby 第三章 method_missing
前回、動的メソッドを説明しました。今回は method_missingをまとめていきます。
mehod_missing
メソッド呼び出しの際に見つからなかった際に呼び出されるメソッドです。 以下にコード例を書いておきます。
class TestMethod end obj = TestMethod.new obj.ghost(2) # undefined method `ghost' for #<TestMethod:0x007fab87884cc8> (NoMethodError)
method_objectは BasicObject(Objectクラスのメソッド kernelメソッドを持たない) で定義されているのでほぼ全てのオブジェクトに対応しています。
使い方
“ドキュメント”を確認すると、 ↓のように書くようです。
def method_missing(method_name [, *args [, &block]])
重要な箇所は以下の点です。 - 第一引数は method名 (method_name) - 第二引数は 引数 or block (*args blocks)
コード例を以下に記載します。
class TestMethod def method_missing(method,input) puts method puts input end end obj = TestMethod.new obj.ghost(2) # ghost 2
method_missingメソッドは メソッド名を引数に持つのでメソッドに応じた処理を 記述することも可能です。
レシーバに対応するメソッドがなく、method_missingで対応した処理を ゴーストメソッド と呼ぶ。
ひとまず、三章ブログはここで一段落としておきます。
動的プロキシ等、残った重要トピックは気が向いた時に追記していきます。
次回は 第四章ブロック やります!
メタプログラミングRuby 第三章メソッド 動的メソッド
動的メソッド
前回は 「動的ディスパッチ」をメインに説明しました。
今回は本丸である動的メソッドの説明を行います。
動的メソッド定義
動的メソッドとは動的にclassとモジュールにメソッドを追加出来ます。
使い方は簡単で「define_method メソッド名」を定義してブロックの中身に 処理コードをつければOKです。
class Hello define_method "hello" do puts "hello" end end obj = Hello.new obj.hello # "hello"
上のコード見ても何言ってるのって感じですよね。 define_methodが効力を発揮するのは同じような処理をまとめるのに 適しています。
例として、たいやきの価格を求めるコードを書いてみます。
メソッドでかく
class Taiyaki def anko_price puts 120 end def cheese_price puts 200 end def cream_price puts 220 end end Taiyaki.new.anko_price
今回は例として「anko・cheese・cream」の3つの味の価格を求めるコードです。 味が何十種類もあったらメソッドを作るの面倒ですよね。
動的メソッド
class Taiyaki_dynamic def initialize hash = {"anko" => 120,"cheese" => 200,"cream" => 220} #①味と価格対応ハッシュ hash.each do |key,val| Taiyaki_dyanamic.price(key,val) #②メソッド作成 end end def self.price(name,price) #クラス・メソッド define_method "#{name}_price" do puts price end end end Taiyaki_dynamic.new.anko_price #120 Taiyaki_dynamic.new.cheese_price #200
今回はmethod作成を self.priceというクラスメソッドにまとめています。 そして initializeメソッド(②)で、クラスをnewした際各味の価格を呼び出すメソッドを作成します。 たいやきの味が増えた場合はhash(①)を増やすだけで良いのでメソッド追加より見通しがよくなります。
メタプログラミングRuby第三章 動的ディスパッチ
重複したプログラムをなくすには?
この際に解決する方法は 動的メソッド と ゴーストメソッド があります。 今回は動的メソッドに絞って説明します。
動的ディスパッチ
動的メソッドの前に、動的ディスパッチを説明します。 動的ディスパッチとはメソッドを呼び出すことが出来る事です。
何がメリットなのか、わかりづらいのでコードを手順にして 説明していきます。
# 1、2、3と出力させる class DyanamicEx def dyanamic_ex1 p "1" end def dyanamic_ex2 p "2" end def dyanamic_ex3 p "3" end end obj = DyanamicEx.new obj.dyanamic_ex1 # 1 obj.dyanamic_ex2 # 2 obj.dyanamic_ex3 # 3
3.times do |i| obj = DyanamicEx.new # 動的ディスパッチ obj.send("dyanamic_ex#{i+1}") #実行dynamic_ex1 ・・・ ex3 end
動的ディスパッチを用いると、動的にメソッドを実行してくれるので が簡素化されていますね。
メタプログラミングRuby第二章(2.2~)
クラスの真相
Classクラスのインスタンスはクラス!
クラスのメソッドはClassクラスのインスタンスメソッド
Classクラスとは?
クラス(ここでいうクラス名は大文字) はClassクラスのインスタンスである。
Stringクラスを例にとると String.class #Class ←Classクラス
moduleとは?
全てのクラスはモジュールである。 Classはインスタンスメソッド(new、allocate、superclass)を追加したモジュールである。 モジュールの使い分けとしては共通的なものはまとめてることが多い。 そしてクラスからmix-inして使えば良い。
定数とは?
定数とは大文字で始まるもの クラス名は定数
定数と変数の違いはスコープ範囲が違う
定数はディレクトリが違えば、一緒ではない、 定数の参照はclass外(含Module)では コロン2つをつけて指定する。 クラス外で定数を定義した場合、暗黙的に Objectクラス の定数になる
module M class C A = "クラス内" p A end A = "モジュール内" p C::A #定数 p A #AA end B = "モジュール外" p M::C::A #クラス内 p M::A #モジュール内 p Object::B #モジュール外 p B #モジュール外 p B.class #String p B.class.superclass #Object
定数が違うと便利なこと
クラス名が衝突してもモジュールで囲えば名前衝突を回避出来る
メタプログラミングRuby二章(2.2途中まで)
第二章(途中まで)書きます。
オープンクラスとは?
既存のクラスを再定義することです。
以下のコードを見て下さい。
これはStringクラスのメソッドを拡張した例です。
class String def dokkiri! return "!" end end p "test".class #String p "test".dokkiri! #!
補足 オブジェクトとClass
僕はこの文読んだ時よくわからなかったです。 なんで、基本的なクラスの話を噛み砕いて説明します 上のソース例の「"test"」は文字列オブジェクトを示しています。 オブジェクトは様々なものありますが、 代表的なものは数値(1や100)、文字列、配列などがあります。
そしてオブジェクトはクラスに属しています。 直感的に言えば性質みたいなものでしょうか。 性質(クラス)に応じて文字列だから「大文字に変換」といった 性質(クラス)に合わせた機能(メソッド)が使えます。 オブジェクトが属するクラスを探すにはclassメソッドを使います。
今回用いたオープンクラスはclass Stringに属するメソッドを追加しました。
しかし、注意があります。
以下のコードを見て下さい。
p "test".capitalize #Test class String def capitalize return self + "!" end end p "test".capitalize # "test!"
オープンクラスは既存のメソッド(capitalize)も変更出来るので 既存のプログラムの挙動が変わり、バグを引き起こすことがあります。 (モンキーパッチという) よって、取扱には十分注意です。
インスタンス変数とは?
インスタンス変数とはクラスをインスタンス化した際に用いる変数です。(先頭に@をつけます) インスタンス変数はクラスに属しているわけではなく、クラスのオブジェクトに紐付いています。
コード例を見てみます。
class Test def test @test = "test" end end obj = Test.new p obj.instance_variables #インスタン変数なし obj2 = Test.new obj.test #@testを定義 p obj.instance_variables #@test
メソッド(ここではtest2)はクラスに属しているため、 クラス(Test)をインスタンス化(オブジェクト化)しただけでは、 メソッド(test)は呼び出されない。 直感的にいうと、Testクラスのインスタンス化はあくまでもメソッド(インスタンスメソッド)の リンクを使えるようにしただけといったところでしょうか。
インスタンス化してすぐにインスタンス変数を使うには 下のコードのようにinitializeメソッドを使う必要があります。
class Test2 def initialize @test2 = "test2" end end obj3 = Test2.new p obj3.instance_variables #@test2
自分の理解が不確かなところもあります。 もし、この記事をみている方で「違うぞ」と思った方は コメントお願い致します。
メタプログラミング1章 準備
第一章は分量も多くないため、頭の整理のため オブジェクト志向についてまとめておきます。
クラス
教科書通りの説明ですと、クラスとは「設計図」です。 よくある例ですと、たい焼きの型ですね オブジェクト志向は再利用を目的としています。
例えば、「たい焼き」を構成するものは 「おおきさ」「素材」「あんこ」「価格」 といったものがあると思います。
ここでは「たい焼き」の情報(素材・中身・個数)を 表示するプログラムを作ります。
今回はクラスを用いて表現してみます。
素材は共通的に用いるため、 クラス変数 を用います。 クラス変数は頭に「@@」をつけます。 代入可能でクラス内での共通的な定数です。
実行方法はclass.newでオブジェクト化して、メソッドを使用します。 initializeはオブジェクトを作成する際に実行した際、自動で呼び出す処理です。intializeは引数を指定することが出来ます。
オブジェクト化した際のメソッド「order」は インスタンスメソッドと呼ばれます。 インスタンスメソッドはクラスから作成されたオブジェクトしか 呼び出すことは出来ません。
class Taiyaki # (素材・中身・個数) @@material = "komugi" #素材は決まっている。 def initialize(order=1,content="azuki") #初期設定 @order_num = order @order_content = content end def order #インスタンスメソッド puts "素材は#{@@material}です。個数は\n#{@order_num}\n中身は#{@order_content}" end end puts Taiyaki.new.order puts Taiyaki.new(2,"チーズ").order
メタプログラミングRubyを読む
はじめに
これからメタプログラミングRubyを読んでわかったことをまとめていきます。
編集方針(随時更新)
- 基礎的なこともまとめる。 オライリー特有の「わかっているだろ?」的記述を出来る限り自分の 中で噛み砕いて説明