Ruby 为构造函数外部的实例变量指定默认值
我的目标是在不使用initialize方法的情况下初始化实例变量。 我有以下代码:Ruby 为构造函数外部的实例变量指定默认值,ruby,inheritance,instance-variables,Ruby,Inheritance,Instance Variables,我的目标是在不使用initialize方法的情况下初始化实例变量。 我有以下代码: class Animal attr_reader :age def initialize(age) @age = age end end class Sheep < Animal attr_accessor :likes def initialize(age) super @likes = [] end end sheep = Sheep.new(5) s
class Animal
attr_reader :age
def initialize(age)
@age = age
end
end
class Sheep < Animal
attr_accessor :likes
def initialize(age)
super
@likes = []
end
end
sheep = Sheep.new(5)
sheep.likes << "grass"
这要优雅得多,因为不需要重新调整签名,但它仍然不完美:当我访问该实例变量时,Ruby不会检查likes
的非nil
-ness吗?有没有一种方法可以在不牺牲运行时或代码优雅性的情况下做到这一点?在最后一个示例中:
class Sheep < Animal
attr_accessor :likes
def likes
@likes || @likes = []
end
end
此外,由于您现在将likes
作为一种记忆方法,而不是实例的属性,因此不需要attr\u访问器(或attr\u reader
),等等
您可以做的一件事是从Animal
的initialize
调用一个方法,为子类提供一个钩子来添加自定义功能:
class Animal
attr_reader :age
def initialize(age)
@age = age
setup_defaults
end
private
def setup_defaults
# NOOP by default
end
end
class Sheep < Animal
attr_accessor :likes
private
def setup_defaults
@likes = []
end
end
作为第三个选项,如果您不介意使用initialize
(您主要关心的可能是更改超类的签名),因为您不关心初始化Sheep
的任何参数,您可以覆盖initialize
,如下所示:
class Sheep < Animal
attr_accessor :likes
def initialize(*)
super
@likes = []
end
end
Sheep
仍然可以正常工作,没有任何变化。使用实验补丁,您可以执行以下操作:
class Zaloop
attr_accessor var1: :default_value, var2: 2
def initialize
self.initialize_default_values
end
end
puts Zaloop.new.var1 # :default_value
- 请注意,下面的补丁是实验性的解决方案,如果您决定在生产中使用它,请小心
模块的修补程序:
Module.module_eval do
alias _original_attr_accessor attr_accessor
def attr_accessor(*args)
@default_values ||= {}
attr_names = []
args.map do |arg|
if arg.is_a? Hash
arg.each do |key, value|
define_default_initializer if @default_values.empty?
@default_values[key] = value
attr_names << key
end
else
attr_names << arg
end
end
_original_attr_accessor *attr_names
end
def define_default_initializer
default_values = @default_values
self.send :define_method, :initialize_default_values do
default_values.each do |key, value|
instance_variable_set("@#{key}".to_sym, value)
end
end
end
end
Module.Module\u eval do
别名\u原始\u属性\u访问器属性\u访问器
def属性存取器(*args)
@默认_值| |={}
属性名称=[]
args.map do | arg|
如果arg.is_a?搞砸
arg.每个do |键、值|
如果@default\u values.empty,定义\u default\u初始值设定项?
@默认值[键]=值
attr_名称真正的问题不是实例变量与类实例变量不同。即使您在类主体中初始化了类变量或常量,在实例方法中使用它(将元素推入)也意味着您正在修改来自不同对象的共享数组。“这似乎没有道理。”sawa我可以问一下这个评论指的是什么吗?我知道在类主体中初始化类变量会给我一种不同的行为,这就是我所说的“其他OO语言可以做到的”。我主要是想用Ruby寻找一个优雅的解决方案,它允许我基本上了解Java中成员变量的行为,而不必担心以后重写代码。@sawa顺便说一句,我自由地重新添加了您所做编辑的一些短语。我同意我的问题太长了,但我觉得这个问题背后的动机有些模糊。(而且,这听起来可能像是我在向Ruby开枪。我实际上是想向我的代码开枪。哎呀)。请不要在生产中使用它。从未。
class Animal
attr_reader :age
def initialize(age)
@age = age
setup_defaults
end
private
def setup_defaults
# NOOP by default
end
end
class Sheep < Animal
attr_accessor :likes
private
def setup_defaults
@likes = []
end
end
def likes
@likes ||= [] # shorter way of doing what you have
end
class Sheep < Animal
attr_accessor :likes
def initialize(*)
super
@likes = []
end
end
class Animal
attr_reader :age, :name
def initialize(name, age)
@name = name
@age = age
end
end
class Zaloop
attr_accessor var1: :default_value, var2: 2
def initialize
self.initialize_default_values
end
end
puts Zaloop.new.var1 # :default_value
Module.module_eval do
alias _original_attr_accessor attr_accessor
def attr_accessor(*args)
@default_values ||= {}
attr_names = []
args.map do |arg|
if arg.is_a? Hash
arg.each do |key, value|
define_default_initializer if @default_values.empty?
@default_values[key] = value
attr_names << key
end
else
attr_names << arg
end
end
_original_attr_accessor *attr_names
end
def define_default_initializer
default_values = @default_values
self.send :define_method, :initialize_default_values do
default_values.each do |key, value|
instance_variable_set("@#{key}".to_sym, value)
end
end
end
end