如何用更合理的东西替换add_entry方法?
class MyStorageClass
def add_entry key, value
eval "(@#{key} ||= []) << value; def #{key}; @#{key}; end"
end
end
那么我可以按如下方式检索值:
def get_entry key
begin
self.send key.to_sym
rescue NoMethodError
nil
end
end
答案 0 :(得分:6)
而不是每个键的实例变量,这需要一些不必要的庞大代码,为什么不是像下面这样的单个哈希。此外,define_method
和define_singleton_method
可以成为您的朋友,以避免糟糕的错误eval
。
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
您可能想要检查您是否首先覆盖了预定义的方法(!@data.key?(key) && self.respond_to?(key)
方法顶部的add_entry
会这样做),但这是另一个对话的问题。 。如果有人试图添加名为inspect
,class
或哦,get_entry
的密钥,可能会很糟糕!
答案 1 :(得分:1)
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"}
答案 2 :(得分:0)
我不确定你称之为“更明智”,但这里是没有eval
s的模板:
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
可以以更易读的方式定义getter:
self.class.send :attr_reader, key \
unless self.class.instance_methods.include?(key)
答案 3 :(得分:0)
可以使用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
而不是为每个变量定义新的实例方法。