LoginSignup
0
0

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