如何將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