Breve versione della domanda: qual è il modo corretto di implementare la clonazione di oggetti con la copia profonda, usando i principi OOP generalmente accettati?
Mi sono imbattuto in questo mentre esaminavo il Motivo di design del prototipo nel libro Pattern di disegni di GoF, ma penso che si applichi alla clonazione di oggetti generici.
Non lo sarebbe, ogni classe deve implementare correttamente il proprio metodo di istanza di deep_copy
, perché ogni classe ha il proprio modo di "passare attraverso" tutti gli elementi, come left
e right
per un albero binario e, a volte, un oggetto A con 2 altri riferimenti a 2 altri oggetti: B e C, può significare un proprio B e C, e quindi anche B e C dovrebbero essere clonati, mentre in alcuni casi, come un oggetto nodo in un grafico, A che ha un riferimento a B e C significa semplicemente che punta a B e C e NON possiede B, C (altri nodi nel grafico potrebbero anche puntare a B e C).
C'è un modo per clonare, che è serializzarlo e non serializzarlo (che dovrebbe essere lo stesso del marshalling dei dati?) ma non gestisce il caso quando l'oggetto non possiede un altro oggetto, o nel caso di un nodo in un grafico, puoi serializzare e unserialize e recuperare un nodo clonato che punta ai nodi appropriati nel grafico come fa l'oggetto nodo originale?
Può sorgere un'altra complicazione, se l'oggetto A ha una variabile di istanza foo
, e ha una struttura dati che l'oggetto di riferimento B
due volte, quindi non dovremmo davvero clonare B due volte. Oppure, se foo
lo fa riferimento una volta nella sua struttura dati, ma un'altra variabile di istanza bar
fa riferimento anche a B
, allora anche non dovremmo clonare B due volte ma una volta. E se A non possiede B, allora non dovremmo clonare B affatto.
Ma diciamo che ignoriamo la complicazione di cui sopra:
Quindi, approssimativamente, tutte le classi nella tua applicazione dovrebbero implementare il proprio metodo di deep_copy
, e questo è grosso modo questo:
# Pseudo code:
class SomeClass
def deep_copy
new_object = self.clone() # to have all the instance variables and
# methods cloned, but just a shallow copy, and
# also, all the inheritance, access to
# class variables, methods, and inheritance
# hierarchy should be properly set up
for all objects that is referenced by my instance variables
if I own the object (by the design of my class), then
# rely on polymorphism to make a proper deep_copy of this object
new_object.this_instance_variable = self.this_instance_variable.deep_copy()
end
end
return new_object
end
end
e a seconda che i tipi primitivi siano o meno oggetti, può semplicemente dire: se possiedo l'oggetto, ma è primitivo, allora non clonarlo. O nel caso in cui i tipi primitivi (come Fixnum, 1, 2, 3) siano anch'essi oggetti, come in Ruby, quindi lasciatelo semplicemente clonare (perché non vuoi eseguire il controllo del tipo per vedere se è clone-in grado) , ma nella riga self.clone
, genererà un'eccezione per dire che questo tipo non è per la clonazione, e in tal caso, cattura l'eccezione e restituisce lo stesso oggetto senza clonarlo (che è il caso base della ricorsione ).
Ma il punto chiave è che, usando i principi OOP generalmente accettati, ogni classe della tua app deve avere la deep_copy
implementata, e il suo contratto (il contratto dell'interfaccia) è che restituirà effettivamente un clone di "me stesso" insieme con copie profonde di oggetti che possiedo (in modo ricorsivo). E potrebbe essere difficile perché molte volte definiamo una classe e non implementiamo realmente un deep_copy
. Se la nostra app ha 12 classi, e abbiamo bisogno di un clone con copia profonda, allora dobbiamo effettivamente implementare tale clone con copia profonda per tutte e 12 le classi (o per tutte le classi che potrebbero aver bisogno di partecipare alla copia profonda). Quanto sopra è corretto o ci sono alcune correzioni secondo i principi OOP?