无需 Marshal 帮助创建对象深拷贝的方法

Methods to create deep copy of objects without the help of Marshal

我有 3 个简单的 classes CashRegister、Bill 和 Position。 CashRegister 由 Bill 对象组成,Bill 对象由 Position 对象组成。它们的实现方式如下

class CashRegister
  def initialize
    @bills = []
  end

  def clone 
    #?
  end
end

class Bill
  def initialize(nr)
    @nr = nr
    @positions = []
  end

  def clone 
    #?
  end
end

class Position
  def initialize(product, price)
    @product = product
    @price = price
  end

  def clone 
    #?
  end
end   

如何创建可以深度复制这些 classes 对象的方法。不允许使用 Marshal.load(Marshal.dump(an_obj))

编辑:到目前为止我得到了这个:

    class CashRegister
      def initialize
        @bills = []
      end

      def clone 
        @bills.map { |bill| bill.clone}
      end
    end

    class Bill
      def initialize(nr)
        @nr = nr
        @positions = []
      end

      def clone 
        cloned = super
        cloned.positions = @positions.map{ |pos| pos.clone}      
        cloned
      end
    end

    class Position

      attr_reader :preis
      # this method is given
      def produkt
        @produkt.clone()
      end

      def initialize(product, price)
        @product = product
        @price = price
      end

      def clone 
        cloned = super
        cloned.product    
        cloned
      end
    end   

class位置的克隆方法似乎没问题(没有编译错误)。但是class Bill中的那个有错误,它说“未定义的方法'positions=',所以问题一定在cloned.positions = @positions.map{ |pos| pos.clone}行。但是我不明白,可以'我们不是这样称呼 cloned.positions 吗?

这只是您需要担心的实例变量。

class Position
  attr_accessor :product, :price
  def initialize(product, price)
    @product = product
    @price = price
  end
end

p1 = Position.new("lima beans", 2.31)
  #=> #<Position:0x000000027587b0 @product="lima beans", @price=2.31>
p2 = Position.new(p1.product, p1.price)
  #=> #<Position:0x0000000273dd48 @product="lima beans", @price=2.31>

我们可以确认p2p1的深拷贝。

p1.product = "lettuce"
p1.price   = 1.49

p1 #=> #<Position:0x0000000271f870 @product="lettuce", @price=1.49>
p2 #=> #<Position:0x000000026e9e00 @product="lima beans", @price=2.31>

p2.product = "spinach"
p2.price = 2.10

p1 #=> #<Position:0x0000000271f870 @product="lettuce", @price=1.49>
p2 #=> #<Position:0x000000026e9e00 @product="spinach", @price=2.1>

如果 class 定义如下(其中 products 是一个数组),则情况会更复杂。

p1 = Position.new ["carrots", "onions"]
  #=> #<Position:0x000000025b8928 @products=["carrots", "onions"]>
p2 = Position.new p1.products
  #=> #<Position:0x000000025b0048 @products=["carrots", "onions"]>

p1.products << "beets"

p1 #=> #<Position:0x000000025b8928 @products=["carrots", "onions", "beets"]>
p2 #=> #<Position:0x000000025b0048 @products=["carrots", "onions", "beets"]>

p2 不是我们想要的。我们需要写

p1 = Position.new ["carrots", "onions"]
  #=> #<Position:0x00000002450900 @products=["carrots", "onions"]>
p2 = Position.new p1.products.dup
  #=> #<Position:0x0000000243aa88 @products=["carrots", "onions"]>

(注意.dup)所以

p1.products << "beets"
  #=> ["carrots", "onions", "beets"]

p1 #=> #<Position:0x00000002450900 @products=["carrots", "onions", "beets"]>
p2 #=> #<Position:0x0000000243aa88 @products=["carrots", "onions"]>

更一般地说,我们需要对实例变量进行深拷贝。

另一个可能的答案是使用 full_dup gem(完全公开,由我编写)然后简单地使用:

p2 = p1.full_dup

现在 full_dup,与常规 dup 一样,不复制任何单例方法。如果这很重要,请尝试 full_clone gem(是的,我也是)。

如果有需要从复制(或克隆过程)中排除的字段,可以定义可选的full_dup_exclude(或full_clone_exclude)方法来列出要从中排除的字段处理中。

请注意,无需担心尝试克隆对象中可能存在的数字、符号和其他不可克隆的内容。 gems.

安全地处理了这些

这个解决方案有效

class CashRegister
  attr_accessor :bills      

  def initialize
    @bills = []
  end

  def clone 
    cloned = super
    cloned.bills = @bills.map { |bill| bill.clone }
    cloned 
  end
end

class Bill
  attr_accessor :positions

  def initialize(nr)
    @nr = nr
    @positions = []
  end

  def clone 
    cloned = super
    cloned.positions = @positions.map{ |pos| pos.clone }      
    cloned
  end
end

class Position
  attr_reader :price
  attr_writer :product

  # this method is given
  def product
    @product.clone
  end

  def initialize(product, price)
    @product = product
    @price = price
  end

  def clone 
    cloned = super
    cloned.product = product  
    cloned
  end
end