在没有Marshal帮助的情况下创建对象深层副本的方法

时间:2017-07-08 18:12:44

标签: ruby oop clone deep-copy

我有3个简单的类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   

如何创建可以深层复制这些类的对象的方法。不允许使用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   

类Position中的clone方法似乎没问题(没有编译错误)。但是在Bill类中有一个错误,它表示“未定义的方法'position =',所以问题必须在cloned.positions = @positions.map{ |pos| pos.clone}行。但我不明白,我们不能调用{{ 1}}那样吗?

3 个答案:

答案 0 :(得分:3)

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

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>

例如,如果将类定义如下(其中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"]>

更一般地说,我们需要制作实例变量的深层副本。

答案 1 :(得分:1)

此解决方案有效

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 

答案 2 :(得分:0)

另一个可能的答案是使用full_dup gem(完全披露,由我编写)然后只需使用:

p2 = p1.full_dup

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

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

请注意,无需担心尝试克隆对象中可能存在的数字,符号和其他不可克隆的东西。这些都是安全处理的宝石。