我有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}}那样吗?
答案 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>
我们可以确认p2
是p1
的深层副本。
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)方法来列出要从处理中排除的字段。
请注意,无需担心尝试克隆对象中可能存在的数字,符号和其他不可克隆的东西。这些都是安全处理的宝石。