我有一个可以解析不同类型消息的类,我想要做的是创建一个散列,它将使用msg类型id作为键,使用不同的实例方法作为值。
这样的事情:
class Parser
def initialize(msg_id)
@my_methods = {1 => method_1, 2 => method_2, 3 => method_3}
@my_methods[msg_id]()
end
def method_1
end
def method_2
end
def method_3
end end
我知道这是可能的,但我不知道该怎么做。我尝试使用self.method(:method_1)
作为值,但我收到一条错误消息,指出method_1
未定义。
谢谢
答案 0 :(得分:1)
虽然提供的答案可以正常使用,但它有一些“次要”问题:
private
和protected
)的任何可见范围定义的所有方法,但对于来说仍然是一个好习惯>仅依靠公共接口,因此,我建议使用Object#public_send
。所以这就是我的建议(尽管事实上我并没有看到这样的地图如何在现实生活中发挥作用):
class Parser
def initialize(msg_id)
# generate a dynamic hash with keys starting with 1
# and ending with the size of the methods count
methods_map = Hash[(1..instance_methods.size).zip(instance_methods)]
# Use public_send to ensure, only public methods are accessed
public_send(methods_map[msg_id])
end
# create a method, which holds a list of all instance methods defined in the class
def instance_methods
self.class.instance_methods(false)
end
end
经过一番思考后我稍微重构了一下,以便隐藏映射到private
方法的实现:
class Parser
def initialize(msg_id)
public_send(methods_map[msg_id])
end
# methods omitted
private
def methods_map # not methods_hash, because what we do is mapping
Hash[(1..instance_methods.size).zip(instance_methods)]
# or
# Hash[instance_methods.each.with_index(1).map(&:reverse)]
end
def instance_methods
self.class.instance_methods(false)
end
end
答案 1 :(得分:1)
修复代码的最简单的更改是这样的:
class Parser
def initialize(msg_id)
@my_methods = { 1 => method(:method_1), 2 => method(:method_2), 3 => method(:method_3) }
@my_methods[msg_id].()
end
def method_1; end
def method_2; end
def method_3; end
end
即。使用Object#method
方法获取Method
对象,并使用Method#call
方法执行它。
但是,我们可以做一些改进。例如,您的Hash
关联Integer
具有值。但是有一个更好的数据结构已经做到了:Array
。 (注意:如果您的邮件ID未按顺序分配,那么Hash
可能是正确的选择,但从您的示例的外观来看,它们仅从Integer
计算1
。)
其次,对Parser#initialize
方法中的方法进行硬编码可能不是一个好主意。应该有协议的声明性描述,即消息ID及其相应的方法名称。
class Parser
# this will make your message IDs start at 0, though
PROTOCOL_MAPPING = [:method_1, :method_2, :method_3].freeze
def initialize(msg_id)
@my_methods = PROTOCOL_MAPPING.map(&method(:method))
@my_methods[msg_id].()
end
def method_1; end
def method_2; end
def method_3; end
end
另一种可能性是:
class Parser
PROTOCOL_MAPPING = []
private_class_method def self.parser(name)
PROTOCOL_MAPPING << name
end
def initialize(msg_id)
@my_methods = PROTOCOL_MAPPING.map(&method(:method))
@my_methods[msg_id].()
end
parser def method_1; end
parser def method_2; end
parser def method_3; end
end
或许这个:
class Parser
PROTOCOL_MAPPING = {}
private_class_method def self.parser(msg_id, name)
PROTOCOL_MAPPING[msg_id] = name
end
def initialize(msg_id)
@my_methods = PROTOCOL_MAPPING.map {|msg_id, name| [msg_id, method(name)] }.to_h.freeze
@my_methods[msg_id].()
end
parser 1, def method_1; end
parser 2, def method_2; end
parser 3, def method_3; end
end
答案 2 :(得分:0)
您正在寻找的方法是send
。
请注意,哈希值必须是要传递给send
的符号。
class Parser
def initialize(msg_id)
@my_methods = {1 => :method_1, 2 => :method_2, 3 => :method_3}
send(@my_methods[msg_id])
end
def method_1
end
def method_2
end
def method_3
end
end