章节索引 :

Ruby 动态调用方法

本章节我们会讲解学习如何在 Ruby 中如何动态调用方法。

1. 如何动态调用方法

我们使用send来动态调用方法。

实例:

class Cloud 

  def rain
    p "rain"
  end 

  def hurricane
    p "hurricane"
  end 

end 

cloud = Cloud.new
cloud.send(:rain)
cloud.send(:hurricane)
p "====================="
cloud.rain
cloud.hurricane


# ---- 输出结果 ----
"rain"
"hurricane"
"###################"
"rain"
"hurricane"

解释:

在此示例中,我们定义了一个具有两个方法的类,然后使用send方法调用了每个方法。为了进行比较,我们还使用点运算符(.)以通常的方式调用了相同的方法。

**注意事项:**方法名的类型通常使用Symbol,字符串也没有问题。

cloud = Cloud.new
cloud.send(:rain)
cloud.send(:hurricane)
p "====================="
cloud.send("rain")
cloud.send("hurricane")

"rain"
"hurricane"
"rain"
"hurricane"

2. 动态调用方法传递参数

2.1 传递往常一样的参数

要使用send将参数发送给方法,我们需要在方法名称后指定参数。我们可以放多少东西没有限制。

实例:

class ParametersTest

  def method_with_positional_parameter(a)
    p a 
  end 

  def method_with_few_positional_parameters(a,b,c)
    p a,b,c
  end 

  def method_with_infinity_args(*a)
    p a 
  end

  def method_with_keyword_parameter(keyword_parameter:)
    p keyword_parameter
  end

end

test = ParametersTest.new 
test.send(:method_with_positional_parameter, "Hello!")
test.send(:method_with_few_positional_parameters, "Hi!", "Hello!", "Hola!")
test.send(:method_with_infinity_args, "first", "second", "third")
test.send(:method_with_keyword_parameter, keyword_parameter: "keywooord parameter")

# ---- 输出结果 ----
"Hello!"
"Hi!"
"Hello!"
"Hola!"
["first", "second", "third"]
"keywooord parameter"

我们可以看到,传递块没有什么区别,在两种情况下,我们收到的结果都是相同的。

2.2 传递一个block

实例:

class Cloud 

  def rain
    p "rain"
    yield
  end 

  def hurricane 
    p "hurricane"
    yield
  end 

end 

cloud = Cloud.new
cloud.send(:rain) { p "It's raining! "}
cloud.send(:hurricane) { p "Wow! I better stay home today!"}
p "###################"
cloud.rain { p "It's raining! "}
cloud.hurricane { p "Wow! I better stay home today!"}


# ---- 输出结果 ----
"rain"
"It's raining! "
"hurricane"
"Wow! I better stay home today!"
"###################"
"rain"
"It's raining! "
"hurricane"
"Wow! I better stay home today!"

传递参数的方式非常明显,方法调用的结构变化不大。

注意事项:

如果方法名称指定不正确,我们将看到一个异常。

实例:

class ExampleClass

  def method_name 
  end 

end

instance = ExampleClass.new 
instance.send(:method_method_name)

# ---- 输出结果 ----
NoMethodError: undefined method `method_method_name' for #<ExampleClass:0x0000000194ff10>
Did you mean?  method_name
	from (irb):34
	from /usr/bin/irb:11:in `<main>'

3. 动态调用方法的实例

当我们想一次调用多个方法时,最适合使用send方法。

想象一下,我们拥有一些包含许多不同引擎的技术,我们需要启动每个引擎。

实例:

class SomeTechnology
  
  def turn_on
    lower_engine
    upper_engine
    left_upper_engine
    right_upper_engine
  end 

  private 

  def lower_engine
    p "lower engine wroom wooom" 
  end 

  def upper_engine 
    p "upper engine wroom wroom"
  end 

  def left_upper_engine
    p "left upper engine wroooooom"
  end 

  def right_upper_engine
    p "right upper engine wroooooom"
  end 
  
end 

我们可以注意到,首先是所有方法名称中都有一个通用词“ engine”。第二个问题是,如果方法数量增加,我们将不得不扩展turn_on方法。

class SomeTechnology

  ENGINES = [:lower, :upper, :left_upper, :right_upper]
  
  def turn_on
    ENGINES.each do |name|
      send("#{name}_engine")
    end 
  end 

  private 

  def lower_engine
    p "lower engine wroom wooom" 
  end 

  def upper_engine 
    p "upper engine wroom wroom"
  end 

  def left_upper_engine
    p "left upper engine wroooooom"
  end 

  def right_upper_engine
    p "right upper engine wroooooom"
  end 
  
end 

tech = SomeTechnology.new 
tech.turn_on

# ---- 输出结果 ---
"lower engine wroom wooom"
"upper engine wroom wroom"
"left upper engine wroooooom"
"right upper engine wroooooom"

这样我们代码的扩展性就变得高了。

4. 小结

本章中我们学习了:

  • send方法在Object类中定义作为参数,我们可以传递字符串或字符。

  • 我们可以传递一个块,就像通常的方法调用一样。

  • 我们也可以像往常一样传递所有相同的参数。

  • 如果方法名称指定不正确,我们将看到一个异常。

  • 当我们动态获取方法名称时,此方法非常理想。

  • 当我们想一次调用多个方法时,send方法是合适的。