java 等静态语言中,我可以用两个相同名称的函数来处理不同类型的入参:
//处理str
public static String handle(String str){
……
}
//处理数组
public static List<String> handle(List<String> arr) {
//代码仅为示意
for(String str : arr){
handle(str)
}
}
当我调用 handle 时,无论什么类型的入参都可以用,很方便。
在 ruby 中,我只能用两个不同名称的函数:
def handle_str(str)
def handle_arr(arr)
我也想过在同一个函数中通过判断入参类型来分别处理,但是感觉还是很麻烦,特别是当 handle_arr 调用了 handle_str 的时候。
有没有更优雅的方式呢?
ruby 不支持 overload。
你举得例子,简单的情况下能写出这样。不过复杂的情况需要具体分析。
def handle(any_type_data)
Array(any_type_data).each do |e|
do_someting
end
end
或许这样吧,之前经常见到的做法
def handle(value)
if value.is_a?(String)
# do_someting
else if value.is_a?(Array)
# do_someting
end
end
就跟 java 的 instanceof 一样判断
参数重载功能,一般是静态强类型语言才会提供的功能,ruby 作为动态强类型语言,采用这种方式会比较怪异,和 ruby 的风格不搭,而且要做种模式,ruby 先要把类型标注做好才行。
转换一下思路,用这种方法是不是也行?
def handle(options={})
str = options.fetch(:str, "hello world")
list = options.fetch(:list, [])
return str unless str.nil?
return list..map {|e| e += 1} unless list.nil?
end
handle(str: 'hello ruby')
handle(list: [1,2,3])
就像楼上说的,一般直接用is_a?
判断就行了,像你说的handle_arr
调用了handle_str
的那种情况,也不冲突,看上去就像是:
def handle(obj)
if obj.is_a? arr
handle_arr(obj)
elsif obj.is_a? str
handle_str(obj)
else
# do_something
end
end
def handle_arr(list)
list.map { |item| handle_str(item) }
end
def handle_str(str)
# do_something
end
有些情况下,用respond_to?
可以获得更好的扩展性,也就是鸭子类型的用法,这里用一个别的例子:
def handle(file)
filename = if file.repond_to? :to_path
file.to_path
elsif file.is_a? String
file
else
# log and raise type not suported exception
end
File.open(file, 'w')
# do_something
end
这样不仅可以处理File
类型的对象,还可以处理其他带有to_path
方法的对象。
当然最理想的情况是直接让参数自己做处理,也就是人们经常说的 Tell, Don't Ask ,比如说
class StringLike
def handle
# do_something
end
end
class ArrayLike
def handle
# do_something
end
end
def handle(obj)
obj.handle
end
array_like = ArrayLike.new
string_like = StringLike.new
handle(array_like)
handle(string_like)
扯远了,说起类型检测就条件反射了
ruby 在类上加方法很容易 可以在 array 和 string 各自定义一个 handle 方法
运行的时候就不是 handle(obj), 而是 obj.handle
这样就更容易实现函数式编程