Qual è un modo corretto per implementare la clonazione di oggetti con la copia profonda, usando i principi OOP generalmente accettati?

3

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?

    
posta 太極者無極而生 03.01.2016 - 10:38
fonte

2 risposte

2

Ne hai il succo.

Va notato che, a seconda della lingua che si usa, potrebbero esistere convenzioni per le quali la funzione esegue una copia profonda di un oggetto.
In molte lingue, la tua funzione deep_copy si chiamerebbe clone . In C ++, questo è anche il territorio del copy-constructor.

    
risposta data 03.01.2016 - 11:14
fonte
0

Esattamente come ha spiegato Bart. Per convenzione in C ++ o Java, questo viene fatto con un metodo di istanza clone() o con un costruttore di copie. In effetti, potrebbe essere una buona pratica, per il codice che si prevede di rilasciare, per fornire entrambi.

public class Foo
{
    private Bar m_bar ;
    private Baz m_baz ;

    // copy constructor
    public Foo( Foo that )
    { // other classes might use either pattern
        this.m_bar = that.m_bar.clone() ;
        this.m_baz = new Baz( that.m_baz ) ;
    }

    // simply reuse copy constructor implementation
    public Foo clone()
    {
        return new Foo( this ) ;
    }
}
    
risposta data 03.01.2016 - 17:30
fonte

Leggi altre domande sui tag