2009-03-19 9 views
7

Posiadam starszą bazę kodu Java (jvm 1.4), która wydaje się używać klonowania jako alternatywy dla tworzenia instancji obiektu. Zgaduję, że to optymalizacja wydajności. Oto zmyślony przykład:Czy klonowanie zapewnia poprawę wydajności w stosunku do konstruktorów/metod fabrycznych?

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 
    } 
} 

zwykłe ostrzeżenia o przedwczesnej optymalizacji pomimo, był to faktycznie zaleca idiom w pewnym momencie?

+0

Wydaje się dziwne. Użyłbym konstruktora do tworzenia nowych obiektów. –

Odpowiedz

3

Jednym z powodów wywołania clone() zamiast konstruktora kopiowania lub metody fabrycznej jest to, że żadna inna opcja nie jest dostępna.

Wdrażanie clone() w celu wykonywania płytkich kopii obiektu (głębsza kopia jest bardziej zaangażowana) jest trywialne w porównaniu do implementacji konstruktora kopii lub metody fabrycznej do wykonania tej samej operacji. Aby zaimplementować clone(), klasa musi po prostu zaimplementować interfejs Cloneable i zastąpić metodę clone() tym, który wywołuje super.clone(), który zwykle wywołuje Object.clone(). Object.clone() kopiuje każdą właściwość oryginalnego obiektu do odpowiedniej właściwości duplikatu, tworząc w ten sposób płytką kopię.

Chociaż wdrożenie clone() jest proste, nadal łatwo jest zapomnieć o wdrożeniu Cloneable. W związku z tym, potencjalne ryzyko użycia obiektu clone() do powielenia obiektu polega na tym, że jeśli klasa tego obiektu zaniedbuje implementację Cloneable i clone(), wywołuje Object.clone() bezpośrednio lub pośrednio, wyrzuci CloneNotSupportedException.

Zobacz ten code example i poprzedni discussion na poor design interfejsu Cloneable.

4

Przypuszczalnie chcieli kopii. Być może chcą przekazać go innej funkcji i nie może być pewien, że funkcja ta go nie zmieni. Jest to sposób upewnienia się, że metoda doStuff() jest stała w odniesieniu do stanu obiektu Foo, do którego jest wywoływana.

+0

Obalenie tego powodu +1 (po spojrzeniu na kod) pojawia się jako bardziej lub bardziej prawdopodobne niż moja bardziej ezoteryczna odpowiedź. – MarkusQ

+0

Należy pamiętać, że jest to podstawowy cel klonowania (zakładając, że moi profesorowie wiedzieli, o czym rozmawiali). – Chris

+0

+1: Tak, wydaje się, że tutaj '.klone()' jest językowym sposobem przekazywania wartości. –

1

Może to być optymalizacja wydajności, w zależności od tego, ile pracy wykonują konstruktorzy.

Jest to bardziej prawdopodobne, ponieważ semantyka jest inna. Klonowanie zapewnia sposób na implementację "prototypowej semantyki" (jak w javascript, self itp.) W języku, który normalnie tak nie wygląda.

+0

Jak to zrobić? Mimo, że prototypowa semantyka oznaczała, że ​​można zmienić zachowanie konstruktora lub innych pól lub metod w środowisku wykonawczym. –

+0

Klonowanie pozwala ustawić wartości początkowe, itd. I (używając delegatów) również zmienić zachowanie. Jest to rodzaj kludge, ale generalnie nie jest to w pełni rozwinięta semantyka Self-style, tylko trochę soku, więc często działa w praktyce. – MarkusQ

0

Jeśli konstruktor SomeObject wykonuje kosztowną pracę, na przykład pobierając coś z bazy danych lub parsując coś lub czytając coś z pliku, klon miałby sens, aby uniknąć wykonywania pracy.

Jeśli konstruktor nie robi nic, to naprawdę nie ma potrzeby używania klona.

Edycja: dodano kod, aby pokazać, że klon nie musi zrobić tę samą pracę jako konstruktor:

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); 
    } 
} 

Główny konstruktor nazywa się raz, pi obliczeniowa jest wykonywana raz.

+0

jeśli zamierzasz oznaczyć coś, przynajmniej komentuj to, co uważasz za niedokładne! – TofuBeer

+0

Jeśli konstruktor kopiowania wykonał kosztowną pracę, klon musiałby zrobić to samo. –

+0

Klon omija konstruktora - jak to zrobić, musiałby zrobić to samo? – TofuBeer

2

Jednym z głównych problemów z konstruktorami kopiowania jest to, że typ obiektu musi być znany podczas kompilacji.Jeśli dziedziczna klasa obsługuje konstruktor kopiowania, a konstruktorowi przekazuje się obiekt klasy pochodnej, konstruktor wytworzy obiekt klasy bazowej, którego właściwości klasy bazowej na ogół odpowiadają właściwościom obiektu przekazanego, ale nowy obiekt wygrał ' • obsługuje wszystkie funkcje obecne w obiekcie przekazanym, które nie były obecne w klasie bazowej.

Możliwe jest rozwiązanie tego problemu przez uczynienie konstruktora kopiującego "chronionym" i posiadanie możliwej do zastąpienia metody kopiowania fabrycznego w każdej wyprowadzonej klasie, która wywołuje ten własny konstruktor kopii klasy, który z kolei wywołuje konstruktora kopiowania swojej bazy klasa. Każda klasa pochodna wymaga jednak konstruktora kopiowania i zastąpienia metody kopiowania, niezależnie od tego, czy dodaje nowe pola. Jeśli klasa sprawy używa "klona", ten dodatkowy kod może zostać wyeliminowany.

Powiązane problemy