Rubyでディープコピー
Rubyでディープコピーをするのに目から鱗な技があります。
deep_copied = Marshal::load(Marshal::dump(target_object))
こんなことができるのもRubyのシリアライザMarshalが非常に優れていて、ほぼ全てのオブジェクトに対応しているからですが、対応し過ぎているために時に思った動作をしないことがあります。例えばディープコピー対象のオブジェクトにシングルトンオブジェクトが含まれていたらどうなると思いますか?当然といえば当然ですが、これらについてもディープコピーが取られてしまい、シングルトン性が破壊されます。
そういうときはMarshalの内部的な動作を規定するためのメソッド、_dumpと_loadをオーバーライドすればよいみたいです(Ver 1.8ではmarshal_dumpとmarshal_load)。コードの例は続きをどうぞ。
class Singleton
def initialize
@a = 1
@b = 2
end
@@obj = Singleton::new
def Singleton::getInstance
return @@obj
end
def _dump(limit)
Marshal::dump(self.instance_variables.collect{|var| instance_variable_get(var)}, limit)
end
def Singleton::_load(obj)
vars = @@obj.instance_variables
Marshal::load(obj).each_with_index{|value, i|
@@obj.instance_variable_set(vars[i], value)
}
return @@obj
end
private :initialize
end
def initialize
@a = 1
@b = 2
end
@@obj = Singleton::new
def Singleton::getInstance
return @@obj
end
def _dump(limit)
Marshal::dump(self.instance_variables.collect{|var| instance_variable_get(var)}, limit)
end
def Singleton::_load(obj)
vars = @@obj.instance_variables
Marshal::load(obj).each_with_index{|value, i|
@@obj.instance_variable_set(vars[i], value)
}
return @@obj
end
private :initialize
end
class Container
def initialize
@singleton_obj = Singleton::getInstance
end
end
c = Container::new
p c
#=> #<Container:0x10196bd0 @singleton_obj=#<Singleton:0x101aca40 @b=2, @a=1>>
p Marshal::load(Marshal::dump(Container::new))
#=> #<Container:0x1018c718 @singleton_obj=#<Singleton:0x101aca40 @b=2, @a=1>>
Singletonのアロケートが変わっていないことに注目してください。
コメント
コメントする
- 匿名でのコメントは受け付けておりません。
- お名前(ハンドル名可)とメールアドレスは必ず入力してください。
- メールアドレスを表示されたくないときはURLも必ず記入してください。
- コメント欄でHTMLタグは使用できません。
- コメント本文に日本語(全角文字)がある程度多く含まれている必要があります。
- コメント欄内のURLと思われる文字列は自動的にリンクに変換されます。
- 投稿ボタンを押してエラーがでなければ、投稿は成功しています。反映されるまでには少し時間がかかります。