2016-05-05 89 views

回答

6

而不是每个键的实例变量,这需要一些不必要的笨重的代码,为什么不只是像下面的单个哈希。此外,define_methoddefine_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的密钥,可能会很糟糕!

+0

你也可以初始化@data与Hash.new {[]},所以你不需要,|| =部分add_entry方法 – karina

+1

危险!它必须是'Hash.new {| h,k | h [k] = []}',否则所有新键获得与其他键相关的值。你传递的[]对象是内存中的一个单独对象,将被所有的键引用,我们使用的'<<'运算符会修改该对象。试试看! 'h = Hash.new([]); h [:x] << 1; h [:y]'给你'[1]'。除了保存'|| =' –

+0

以外,我通常会选择使用哈希的块初始化程序,但我确实使用了大括号。 – karina

0

我不知道你所说的“更合理”,但这里是模板而不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) 
0

这可以通过使用instance_variable_setattr_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,而不是定义为每个变量一个新的实例方法。

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"} 
+0

['Hashie :: Mash'](https://github.com/intridea/hashie#mash)? – mudasobwa

+0

@mudasobwa上下文/细化,请问 –

+0

我只是想放下一个伟大的图书馆的链接,那就是[恕我直言]所有的意思都是更好的“OpenStruct”。 – mudasobwa