的問題似乎歸結爲這樣:給出如下:
class A
attr_reader :instance_var
def initialize
@instance_var = (@instance_var ||= 0) + 1
instance_local_var = 33
puts "instance_local_variables = #{ local_variables }"
instance_local_var = 33
end
class_local_var = 3
puts "class_local_variables = #{ local_variables }"
class_local_var = 3
end
# class_local_variables = [:class_local_var]
#=> 3
可以在一個確定的instance_local_var
和class_local_var
值是多少?
確定class_local_var
的價值這個問題的答案顯然是「不」,因爲class_local_var
不再存在(已被標記爲垃圾收集)end
執行後:
A.send(:local_variables)
#=> []
確定instance_local_var
的值
a = A.new
# instance_local_variables = [:instance_local_var]
#=> #<A:0x007ff3ea8dbb80 @instance_var=1>
請注意,@instance_var #=> 1
。
A.new
不返回的instance_local_var
值,但由於該變量在initialize
最後一行分配值,可以通過再次執行initialize
來獲得該值。
instance_local_var = a.send(:initialize)
#=> 33
有個問題,但是:
a.instance_var
#=> 2
執行initialize
的第二時間已經引起不想要的副作用。我對initialize
的定義是人爲的,但它強調了可以通過第二次執行initialize
來發生許多不良副作用的事實。
現在讓我們來獲取一個新實例。
b = A.new
# instance_local_variables = [:instance_local_var]
#=> #<A:0x007fee0996e7c8 @instance_var=1>
再次,@instance_var=1
。對給定實例調用initialize
兩次的副作用的一種可能的解決方法是對子類A
進行子類化並使用super
。
class B < A
attr_reader :b
def initialize
@b = super
end
end
B.new.b
#=> 33
a.instance_var
#=> 1
沒有保證不良副作用可以用這種方法來避免(例如,initialize
任何情況下可以執行應該只出現一次數據庫操作),但它似乎離開初始實例a
不受影響。這當然都是假設的。
1. send
因爲A.private_methods.include?(:local_variables) #=> true
A.new.send(:initialize)
2是必需的因爲iniitialize
是私人必須使用。
你可以嘗試在類級別定義'def a =(val)'。這將抓住它,但不知道你如何使它像一個正常的本地任務一樣工作。 – mahemoff