如何将add_entry方法替换为更明智的方法?Ruby动态创建方法和变量
def get_entry key
begin
self.send key.to_sym
rescue NoMethodError
nil
end
end
如何将add_entry方法替换为更明智的方法?Ruby动态创建方法和变量
def get_entry key
begin
self.send key.to_sym
rescue NoMethodError
nil
end
end
而不是每个键的实例变量,这需要一些不必要的笨重的代码,为什么不只是像下面的单个哈希。此外,define_method
和define_singleton_method
可以是你的朋友,以避免不好的坏eval
s。
class MyStorageClass
def initialize
@data = {}
end
def add_entry(key, value)
(@data[key] ||= []) << value
define_singleton_method(key){ @data[key] }
end
def get_entry(key)
@data.key?(key) or raise NoMethodError
@data[key]
end
end
你可能要检查你没有(在add_entry
方法都不能做到的顶部[email protected]?(key) && self.respond_to?(key)
)覆盖预定义的方法首先,但那是另一个谈话。例如,如果有人试图添加名为inspect
,class
或哦,get_entry
的密钥,可能会很糟糕!
我不知道你所说的“更合理”,但这里是模板而不eval
s到开始::
class MyStorageClass
def add_entry key, value
eval "(@#{key} ||= []) << value; def #{key}; @#{key}; end"
end
end
于是我可以如下检索值
def add_entry key, value
# define instance variable unless it is already defined
instance_variable_set :"@#{key}", [] \
unless instance_variable_defined? :"@#{key}"
# add value to the array
instance_variable_set :"@#{key}", instance_variable_get(:"@#{key}") + value
# define getter
self.class.send :define_method key { instance_variable_get :"@#{key}" } \
unless self.class.instance_methods.include?(key)
end
吸气剂可能以更易读的方式定义:
self.class.send :attr_reader, key \
unless self.class.instance_methods.include?(key)
这可以通过使用instance_variable_set
和attr_accessor
来实现:
class MyStorageClass
def add_entry(key, value)
if respond_to?(key)
key << value
else
instance_variable_set("@#{key}", [value])
self.class.send(:attr_accessor, key)
end
end
end
然而如其他人所说,一个更简洁的方法就是使用一个Hash
,而不是定义为每个变量一个新的实例方法。
IMO这是一个非常糟糕的想法。不要这样做!你将会增加复杂性,而且收益甚微。
我推荐改为OpenStruct
。这些都是伟大的对象 - 您可以随意调用getter和setter,而无需事先指定属性。也许有点低效,但通常并不重要。
OpenStruct的一个好处是您可以将您的属性分组为逻辑集,例如, connection_options,formatting_options,等等。这里是一个示例脚本来说明:
#!/usr/bin/env ruby
require 'ostruct'
class MyClass
attr_reader :config_options # only if you want to expose this
def initialize
@config_options = OpenStruct.new
end
def do_something
config_options.color = 'yellow'
config_options.size = 'medium'
end
def to_s
config_options.to_h.to_s
end
end
my_class = MyClass.new
my_class.do_something
puts my_class # outputs: {:color=>"yellow", :size=>"medium"}
你也可以初始化@data与Hash.new {[]},所以你不需要,|| =部分add_entry方法 – karina
危险!它必须是'Hash.new {| h,k | h [k] = []}',否则所有新键获得与其他键相关的值。你传递的[]对象是内存中的一个单独对象,将被所有的键引用,我们使用的'<<'运算符会修改该对象。试试看! 'h = Hash.new([]); h [:x] << 1; h [:y]'给你'[1]'。除了保存'|| =' –
以外,我通常会选择使用哈希的块初始化程序,但我确实使用了大括号。 – karina