0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Meta-3章-メソッド

Last updated at Posted at 2017-03-25

動的dispatch

  • method名が引数のため、実行時にmethodを決められる
  • 動的に呼び出す
class MyClass
	def my_method(my_arg)
		my_arg * 2
	end
end

obj = MyClass.new
obj.my_method(3)
# 6
obj.send(:my_method, 3)
# 6

動的method

  • define_methodを使うとmethod実行時にmethod名を決められる
  • 動的に定義する
class MyClass
	define_method :my_method do |my_arg|
		my_arg * 3
	end
end

obj = MyClass.new
obj.my_method(2)

リファクタリング

original code

class Computer
	def initialize(computer_id, data_source)
		@id = computer_id
		@data_source = data_source
	end
	
	def mouse
		info = @data_source.get_mouse_info(@id)
		price = @data_source.get_mouse_price(@id)
		info = "Mouse: #{info} (#{price})"
		return "* #{result}" if price >= 100
		result
	end
	
	def cpu
		info = @data_source.get_cpu_info(@id)
		price = @data_source.get_cpu_price(@id)
		info = "Mouse: #{info} (#{price})"
		return "* #{result}" if price >= 100
		result
	end

	def keyboard
		info = @data_source.get_keyboard_info(@id)
		price = @data_source.get_keyboard_price(@id)
		info = "Mouse: #{info} (#{price})"
		return "* #{result}" if price >= 100
		result
	end
end

動的dispatch適用

  • sendを使ってmethodを動的に呼び出している
  • @data_source.send "get_#{name}_info" , @id
class Computer
	def initialize(computer_id, data_source)
		@id = computer_id
		@data_source = data_source
	end
	
	def mouse
		component :mouse
	end
	
	def cpu
		component :cpu
	end

	def keyboard
		component :keyboard
	end	

	def component(name)
		info = @data_source.send "get_#{name}_info" , @id
		price = @data_source.send "get_#{name}_price", @id
		info = "#{name.capitalize}: #{info} (#{price})"
		return "* #{result}" if price >= 100
		result
	end
end

動的methodを使う

class Computer
	def initialize(computer_id, data_source)
		@id = computer_id
		@data_source = data_source
	end

	def self.define_component(name)
		define_method(name) do
			info = @data_source.send "get_#{name}_info" , @id
			price = @data_source.send "get_#{name}_price", @id
			info = "#{name.capitalize}: #{info} (#{price})"
			return "* #{result}" if price >= 100
			result
		end
	end

	define_component :mouse
	define_component :cpu
	define_component :keyboard
end

さらに

  • String#grepにブロックを渡すと、正規表現にマッチした要素に対しブロックを実行する
  • 正規表現にマッチした文字列は$1に格納される
class Computer
	def initialize(computer_id, data_source)
		@id = computer_id
		@data_source = data_source
		data_source.methods.grep(/^get_(.*)_info$/) { Computer.define_component $1 }
	end
	
	def self.define_computer(name)
		define_method(name) do
			info = @data_source.send "get_#{name}_info", @id
			price = @data_source.send "get_#{name}_price", @id
			result = "#{name.capitalize}: #{info} (#{price})"
			return "#{result}" if price >= 100
			result
		end
	end
end

ゴーストメソッド

  • 常にsuperを呼び出す
  • 常にrespond_method_missing?を再定義する
  • 本物のメソッドではない
  • メソッド呼び出しを途中で捕まえているだけ
  • 実際のメソッドとは振る舞いが異なる
  • Object#methodの結果一覧に登場しない
  • メソッド呼び出しが大量にあったり、呼び出すメソッドが実行時にしかわからない場合に使う

動的メソッド

  • 通常のメソッド
  • defの代わりに、define_methodで定義されたところが違うだけ

動的メソッドを使ったリファクタリング

class Computer
  def initialize(computer_id, data_source)
    @id = computer_id
    @data_source = data_source
    data_source.methods.grep(/^get_(.*)_info$/) { Computer.define_component $1 }
  end

  def self.define_component(name)
    define_method(name) do
      info = @data_source.get_keyboard_info(@id)
      price = @data_source.get_keyboard_price(@id)
      result = "keyboard: #{info} ($#{price})"
      price >= 100 ? "* #{result}" : result
    end
  end
end

Computer クラスを method_missing でリファクタリング

class Computer
  def initialize(computer_id, data_source)
    @id = computer_id
    @data_source = data_source
  end

  def method_missing(name)
    super if !@data_source.respond_to?("get_#{name}_info")
    info = @data_source.send("get_#{name}_info", @id)
    price = @data_source.send("get_#{name}_price", @id)
    result = "#{name.capitalize}: #{info} ($#{price})"
    price >= 100 ? "* #{result}" : result
  end

  def respond_to_missing?(method, include_private = false)
    @data_source.respond_to?("get_#{method}_info") || super
  end
end
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?