5/15/2007

心愛的 Object 變心啦

剛剛發現一個很有趣的事情,寫 code 的時候,有兩個 Model :Person 跟 Setting ,他們呈現 1: 1 關係。結果當我想要創立一個新的 Person 的時候,為了希望能夠簡化設計,我就將其中一個人 a 當作預設值,每個新增 Person 的 Setting 直接 copy 一份過去,所以我就這樣寫




a = Person.find(1)
b = Person.new( :name => 'lala' )
b.setting = a.setting
b.save

我真的沒想太多,但是慘劇就發生了。a.setting 就變心到 b 去了。

>> a = User.find 1
=> #<Person:0x371ee38 @attributes={"updated_at"=>"2007-05-15 22:55:45", "nname"=>"hemidemi lala", "type"=>"GroupAdmin", "id"=>"1", "password"=>"123", "created_at"=>"2007-05-09 00:46:35"}>
>> a.setting
=> nil

也就是他自動的幫你把 a.setting 的 model 裡面的 foreign key 指定到新增的 b ,然後一去不回頭。要預防 object 變心方法也很簡單,就是幫新人找一份完全一模一樣的新伴侶即可。

a = Person.find(1)
b = Person.new( :name => 'lala' )
b.setting = a.setting.clone
b.save

Clone 是 Ruby object 裡面的 method ,作法就是 copy 一份新的 instance。雖然在實際使用上, 其實 clone 並不會直接 new 一份真的 instance,而是 new 一個 object ,然後將裡面 attribute 直接 reference 過去[註1]。但是在 ActiveRecord 裡面使用,因為是直接寫回資料庫,所以就完全沒副作用,所以可以盡量大方的使用。

註1

irb(main):001:0> class Klass
irb(main):002:1> attr_accessor :str
irb(main):003:1> end
irb(main):004:0> s1 = Klass.new => #<Klass:0x89e7c>
irb(main):007:0> s1.str = 'Hello' => "Hello"
irb(main):008:0> s2 = s1.clone => #<Klass:0x79d38 @str="Hello">
irb(main):009:0> s1.object_id => 282430
irb(main):010:0> s2.object_id => 249500
irb(main):011:0> s1.str.object_id => 261360
irb(main):012:0> s2.str.object_id => 261360

我們可以發現到 s1 跟 s2 其實是兩個不同的 object ,但是裡面的 attribute str 卻是同一個 object。




2 則留言:

Plumm 提到...

唔唔...Ruby 的 Object 還是要 Reference 的, 必要時要 clone. (這點和 Java 及其他語言似乎一樣)

joehwang 提到...

對不起,問個笨問題@@
>> a.setting
=> nil

請問a.setting為什麼是nil,a.setting不是會關聯到叫setting的table嗎?