La clonazione offre un miglioramento delle prestazioni rispetto ai metodi di costruzione / fabbrica?

StackOverflow https://stackoverflow.com/questions/663079

Domanda

Sto mantenendo una vecchia base di codice Java (jvm 1.4) che sembra usare la clonazione come alternativa all'istanza di oggetti, immagino come ottimizzazione delle prestazioni. Ecco un esempio inventato:

public class Foo {
  private SomeObject obj; // SomeObject implements Cloneable
  public Foo() {
    obj = new SomeObject();
    obj.setField1("abc"); // these fields will have the same value every time
    obj.setField2("def");
  }
  public void doStuff() {
    SomeObject newObj = obj.clone(); // clone it instead of using a factory method
    // do stuff with newObj
  }
}

Nonostante i soliti avvertimenti sull'ottimizzazione prematura, a un certo punto questo è stato in realtà un linguaggio raccomandato?

È stato utile?

Soluzione

Un motivo per invocare clone() invece del costruttore della copia o di un metodo factory è che nessuna delle altre opzioni potrebbe essere disponibile.

L'implementazione di Cloneable per eseguire una copia di oggetto superficiale (la copia di profondità è più coinvolta) è banale rispetto all'implementazione di un costruttore di copie o di un metodo di fabbrica per eseguire la stessa operazione. Per implementare super.clone(), una classe deve semplicemente implementare l'interfaccia Object.clone() e sostituire il metodo CloneNotSupportedException con uno che invoca <=> che di solito invoca <=>. <=> copia ogni proprietà dell'oggetto originale nella corrispondente proprietà del duplicato, creando così una copia superficiale.

Sebbene l'implementazione di <=> sia semplice, è comunque facile dimenticare di implementare <=>. Di conseguenza, un potenziale rischio di utilizzo di <=> per duplicare un oggetto è che se la classe di quell'oggetto trascura di implementare <=> e <=> invoca <=> direttamente o indirettamente, si getta <=>.

Vedi questo esempio di codice e una precedente discussione su il design scadente di l'interfaccia <=>.

Altri suggerimenti

Presumibilmente volevano una copia. Forse vogliono passarlo a un'altra funzione e non possono essere sicuri che tale funzione non la cambierà. È un modo per assicurarsi che il metodo doStuff () sia const rispetto allo stato dell'oggetto Foo su cui è chiamato.

Un grave problema con i costruttori di copie è che il tipo di oggetto deve essere noto al momento della compilazione. Se una classe ereditabile supporta un costruttore di copie e al costruttore viene passato un oggetto di classe derivata, il costruttore produrrà un oggetto di classe base le cui proprietà di classe base generalmente corrispondono a quelle dell'oggetto passato, ma il nuovo oggetto vincerà ' t supporta tutte le funzionalità presenti nell'oggetto passato che non erano presenti nella classe base.

È possibile risolvere un po 'questo problema creando un costruttore di copie " protetto " e avendo un metodo di copia di fabbrica sostituibile in ogni classe derivata che chiama quel costruttore di copie della classe, che a sua volta chiama il costruttore della copia della sua classe base. Ogni classe derivata avrà bisogno di un costruttore di copia e di una sostituzione del metodo di copia, indipendentemente dal fatto che aggiunga o meno nuovi campi. Se la classe case usa & Quot; clone & Quot; questo codice aggiuntivo può essere eliminato.

Potrebbe essere un'ottimizzazione delle prestazioni, a seconda di quanto lavoro viene svolto nei costruttori.

È più probabile che venga utilizzato perché la semantica è diversa. La clonazione fornisce un modo per implementare & Quot; prototipo semantica & Quot; (come in javascript, self, ecc.) in una lingua che normalmente non tende in questo modo.

Se il costruttore SomeObject fa un lavoro costoso, come prendere qualcosa da un database o analizzare qualcosa o leggere qualcosa da un file, il clone avrebbe senso evitare di fare il lavoro.

Se il costruttore non fa nulla, non è necessario utilizzare il clone.

Modifica: aggiunto codice per mostrare che il clone non deve fare lo stesso lavoro del costruttore:

class Main
    implements Cloneable
{
    private final double pi;

    public Main()
    {
        System.out.println("in Main");
        // compute pi to 1,000,000,000 decimal palaces
        pi = 3.14f;
    }

    public Object clone()
    {
        try
        {
            return (super.clone());
        }
        catch(final CloneNotSupportedException ex)
        {
            throw new Error(); // would not throw this in real code
        }
    }


    public String toString()
    {
        return (Double.toString(pi));
    }

    public static void main(String[] args)
    {
        final Main a;
        final Main b;

        a = new Main();
        b = (Main)a.clone();

        System.out.println("a = " + a);
        System.out.println("b = " + b);
    }
}

Il costruttore principale viene chiamato una volta, il pi di calcolo viene eseguito una volta.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top