はじめに
最近「メタプログラミングRuby」という本を読んで、動的メソッド呼び出しと動的メソッド定義について学習しました。
そこで、実際に2つ使って簡単なコードを書き本記事を作成しました。
前提
動的ではないメソッド定義とメソッド呼び出しは以下のようなものです。
class Hoge
attr_accessor :name
def print_name
puts "name: #{@name}"
end
end
hoge = Hoge.new
hoge.name = "Alice"
hoge.print_name
#=> name: Alice
特徴はそれぞれ次の通りです。
- 固定の名前でメソッドを定義する
- 固定の名前でメソッド呼び出しをする
動的メソット呼び出し
hoge.print_name
hoge.print_type
hoge.print_age
上のようにprint_○○
という命名のメソッドを連続して呼び出す場合について考えます。
そこで、変化する部分を配列で持ちsendメソッドと式展開を利用し動的に記述することが、動的メソット呼び出しです。
columns = ['name', 'type', 'age']
columns.each do |column|
hoge.send("print_#{column}")
end
動的メソッド定義
逆に類似の処理を行うメソッドは以下のようにdefine_methodを利用することで動的に記述できます。これが動的メソッド定義です。
class Hoge
attr_accessor :name, :type, :age
def self.print_item(name)
define_method(name) do
column = name.split('_').last
puts "#{column}: #{self.send(column)}"
end
end
['name', 'type', 'age'].each do |column| # 動的メソッド呼び出しでメソッド定義
print_item "print_#{column}"
end
end
hoge = Hoge.new
{name: 'Alice', type: 'AB', age: 20}.each do |key, value| # 動的メソッド呼び出しで変数を定義する
hoge.send("#{key}=", value)
end
hoge.print_name
#=> name: Alice
hoge.print_type
#=> type: AB
hoge.print_age
#=> age: 20
感想
コードを書く前はどちらもDryにコードを書く役に立ちそうだと感じていました。
しかし、実際にコードを書いてみると、動的メソッド定義の方は、そもそも類似処理をするメソッドを定義すること自体がDryではない気がしました。(下のように単一メソッドで書いた方が良いかも?)
公開済みGemなどメソッド名は変更できない状況で、リファクタリングをする際などは利用できそうです。
class Hoge
attr_accessor :name, :type, :age
def print_column(column)
puts "#{column}: #{self.send(column)}"
end
end
# (略)
hoge.print_column('name')
#=> name: Alice
動的メソッド呼び出しの方は、動的メソッド定義以降のコードでも多様できたので、とても役に立ちそうです。