2017-07-08 6 views
2

J'ai 3 classes simples CashRegister, Bill et Position. Un CashRegister est composé d'objets Bill et un objet Bill est composé d'objets Position. Ils sont implémentés comme suitMéthodes pour créer une copie profonde des objets sans l'aide du Maréchal

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 

Comment créer des méthodes pouvant copier en profondeur les objets de ces classes. L'utilisation de Marshal.load(Marshal.dump(an_obj)) n'est pas autorisée.

Edit: Jusqu'à présent, j'ai ceci:

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 

La méthode clone en position de classe semble être ok (pas d'erreur de compilation). Mais il y a une erreur dans le projet de loi en classe, il est dit "position indéfinie" = ", donc le problème doit être sur la ligne cloned.positions = @positions.map{ |pos| pos.clone}." Mais je ne comprends pas, ne pouvons-nous pas appeler cloned.positions comme ça?

Répondre

1

Cette solution fonctionne

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

Il est juste les variables d'instance que vous avez à vous soucier.

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> 

Nous pouvons confirmer que p2 est une copie en profondeur de 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> 

Il est plus complexe si, par exemple, la classe était défini comme suit (où products est un tableau).

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 est pas ce que nous voulons. Il faudrait écrire

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

(notez le .dup) de telle sorte que

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

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

Plus généralement, nous avons besoin de faire des copies profondes des variables d'instance.

0

Une autre réponse possible est d'utiliser la pierre précieuse full_dup (divulgation complète, écrite par moi), puis utilisez simplement:

p2 = p1.full_dup 

Maintenant full_dup, comme dup régulière, ne copie pas de méthodes singleton. Si c'est important, essayez la gemme full_clone (yup, par moi aussi) à la place.

Si certains champs doivent être exclus du processus dup (ou clone), la méthode facultative full_dup_exclude (ou full_clone_exclude) peut être définie pour répertorier les champs à exclure du traitement. Notez qu'il n'est pas nécessaire de vous soucier d'essayer de cloner des nombres, des symboles et d'autres éléments non clonables qui peuvent exister dans votre objet. Ceux-ci sont manipulés en toute sécurité être les pierres précieuses.