Será que a clonagem fornecem uma melhoria de desempenho ao longo de construtores / métodos de fábrica?

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

Pergunta

Estou maintaing uma antiga base de código Java (JVM 1.4), que parece usar a clonagem como uma alternativa ao objeto instanciação, eu estou supondo como uma otimização de desempenho. Aqui está um exemplo artificial:

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

As advertências usuais sobre otimização prematura, não obstante, era isso realmente um idioma recomendado em algum momento?

Foi útil?

Solução

Uma das razões para clone() invocar em vez do construtor de cópia ou um método de fábrica é que nenhuma das outras opções podem estar disponíveis.

A implementação clone() para executar uma cópia objeto rasa (cópia em profundidade é mais envolvido) é trivial em comparação com a implementação de um construtor de cópia ou de um método de fábrica para realizar a mesma operação. Para implementar clone(), uma classe precisa simplesmente implementar o método de interface Cloneable e override clone() com um que invoca super.clone() que geralmente invoca Object.clone(). Object.clone() cópias cada propriedade do objeto original para a propriedade correspondente da segunda via, criando assim uma cópia superficial.

Embora clone() implementação é simples, ainda é fácil esquecer de implementar Cloneable. Consequentemente, um risco potencial do uso clone() para duplicar um objeto é que se a classe do objeto faz negligenciar a implementar Cloneable e clone() invoca Object.clone() direta ou indiretamente, ele vai jogar CloneNotSupportedException.

Veja este e uma discussão em má concepção do de a interface Cloneable.

Outras dicas

Provavelmente eles queriam uma cópia. Talvez eles querem passá-lo para outra função, e não pode ter certeza que essa função não vai mudar isso. É uma maneira de certificar-se que o método doStuff () é const com relação ao estado do objeto Foo ele é chamado por diante.

Um dos principais problemas com construtores de cópia é que o tipo do objeto tem que ser conhecido em tempo de compilação. Se uma classe hereditária suporta um construtor de cópia, e o construtor é passado um objeto da classe derivada, o construtor irá produzir um objeto da classe base cujas propriedades da classe base geralmente correspondem aos do passado-in objeto, mas o novo objeto de won' t apoiar quaisquer recursos que estavam presentes no passado-in objeto que não estavam presentes na classe base.

É possível resolver este problema um pouco, fazendo um construtor de cópia "protegido", e com um método de cópia fábrica substituível em cada classe derivada que chama próprio construtor de cópia dessa classe, que por sua vez chama o construtor de cópia da sua classe base . Cada classe derivada vai precisar de um construtor de cópia e uma substituição do método de cópia, no entanto, se é ou não acrescenta quaisquer novos campos. Se o usos de classe case "clone", este código extra pode ser eliminado.

Pode ser uma otimização de desempenho, dependendo de quanto trabalho é feito nos construtores.

É mais provável usado porque a semântica são diferentes. Clonagem fornece uma maneira de implementar "protótipo semântica" (como em javascript, auto, etc.) em uma língua que normalmente não tendem dessa forma.

Se o construtor SomeObject funciona caros, tais como pegar algo de um banco de dados ou analisar algo, ou ler algo de um arquivo, em seguida, o clone faria sentido para evitar fazer o trabalho.

Se o construtor não faz nada, então não há realmente nenhuma necessidade de clone uso.

Editar: código adicionado para mostrar que clone não tem que fazer o mesmo trabalho que o construtor:

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

O construtor principal é chamado uma vez, o pi computação é realizada uma vez.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top