LoginSignup
0
0

More than 3 years have passed since last update.

【小ネタ】自前で deep_dup -> 結論、ちょっとやり過ぎ

Last updated at Posted at 2020-04-22

何でもかんでも deep_dup

ActiveSupport との違い。

  • インスタンス変数も漏れなく deep_dup します。
  • Array の場合も自身を dup して、レシーバが Array の子クラスなら、そのクラスのオブジェクトを返します。
  • Hash のキーを dup することは考えていません。
  • Struct にも対応しました。
module DeepDup
  refine Object do
    def deep_dup
      return self if private_methods(false).include?(:dup)

      dup.tap do |copy|
        copy.instance_variables.each do |var|
          val = copy.instance_variable_get(var)
          copy.instance_variable_set(var, val.deep_dup)
        end
      end
    end
  end

  refine Array do
    def deep_dup
      dup.tap { |ary| ary.map!(&:deep_dup) }
    end
  end

  refine Hash do
    def deep_dup
      dup.tap do |hash|
        hash.each { |key, val| hash[key] = val.deep_dup }
      end
    end
  end

  refine Struct do
    def deep_dup
      dup.tap do |struct|
        struct.each_pair { |key, val| struct[key] = val.deep_dup }
      end
    end
  end
end

考察

ActiveSupport は、どうしてインスタンス変数を deep_dup しないのだろう。

何でもかんでも dup するのはやり過ぎなので、dup が必要なインスタンス変数に限って、initialize_dupdup の挙動だけ変更する。)や initialize_copydupclone どちらの挙動も変更する。)で dup なり、deep_dup するようにして、オブジェクト本体の dup の挙動を変更するようにした方が、柔軟な設計が可能だからなのだろうか。

例としては、

class Foo
  def initialize
    @ary = []
  end

  def initialize_copy(orig)
    @ary = orig.instance_variable_get(:@ary).deep_dup
  end
end

こんな感じ。よくよく考えると、なんでもかんでも deep_dup すると、循環連結リストの処理がいつまでも終わらないことに気付いた。

拙い表現ではあるが、ActiveSupport では、インスタンス変数を持たない Array や Hash に関しては deep_dup すること自体が眼目であるのに対し、Object で deep_dup を定義しているのは、dup が禁止されているオブジェクトかどうかを見極めることが主要な目的だということなのだろうか。

結論

インスタンス変数を何でもかんでも deep_dup するのは、やり過ぎでした。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0