O padrão de design Prototype tem como objetivo principal permitir a criação de novos objetos por meio da cópia de um objeto existente, conhecido como protótipo.
Este padrão é usado para resolver problemas específicos relacionados à criação de objetos, especialmente em ==situações onde a instanciação direta (usando o operador new) não é a opção mais eficiente ou adequada==. Os problemas que o padrão Prototype busca resolver incluem:
-
Criação de Objetos Custosa: Quando a criação de um novo objeto é mais custosa em termos de recursos e tempo (por exemplo, quando requer uma série de operações, como leitura de dados de uma base de dados, operações de rede ou processamento intensivo), o Prototype oferece uma alternativa eficiente, permitindo a criação de um objeto a partir de uma cópia de um objeto existente.
-
Evitar Subclasses de Fábrica: Em vez de usar uma hierarquia de classes de fábrica ou um construtor complexo, o Prototype permite criar variantes de objetos por meio da clonagem de um protótipo pré-configurado.
-
Preservação do Estado Interno: Em algumas situações, é necessário que um objeto seja criado com um estado que corresponda ao de um objeto existente. O Prototype permite clonar o estado interno do objeto existente, evitando expor e duplicar a lógica de inicialização.
-
Abstração do Código de Criação: Ao utilizar o padrão Prototype, o cliente pode ser abstraído do processo de criação dos objetos. O cliente precisa conhecer apenas o protótipo e o mecanismo de clonagem.
-
Flexibilidade em Adicionar ou Remover Objetos em Tempo de Execução: Com o Prototype, é possível adicionar ou remover protótipos em tempo de execução, aumentando a flexibilidade do sistema.
-
Instanciação de Classes Dinâmicas: Quando as classes de objetos que precisam ser criados são determinadas em tempo de execução, como em sistemas que precisam de extensibilidade ou carga de módulos dinâmicos.
Em resumo, o padrão Prototype é uma solução para situações em que a criação direta de objetos é inadequada ou ineficiente. Ele oferece um meio de criar objetos por meio da clonagem, o que pode ser especialmente útil em sistemas complexos onde os custos de criação de objetos são altos ou a flexibilidade na criação de novas instâncias é uma exigência chave.
Exemplo de implementação
- Definir uma interface com método clone
- Criar uma classe que implementa a interface.
- Na implementação do método clone retornar a instanciação de um novo objeto com os parâmetros do construtor preenchidos.
Definindo a interface Prototype
public interface Prototype {
Prototype clone();
}
Implementando a interface em uma classe concreta
public class Carro implements Prototype {
private String marca;
private String modelo;
public Carro(String marca, String modelo) {
this.marca = marca;
this.modelo = modelo;
}
// Métodos getters e setters
@Override
public Prototype clone() {
return new Carro(marca, modelo);
}
@Override
public String toString() {
return "Carro{" + "marca='" + marca + '\'' + ", modelo='" + modelo + '\'' + '}';
}
}
Utilizando o Prototype para clonar objetos
public class Main {
public static void main(String[] args) {
Carro carroOriginal = new Carro("Toyota", "Corolla");
System.out.println("Carro Original: " + carroOriginal);
Carro carroClonado = (Carro) carroOriginal.clone();
System.out.println("Carro Clonado: " + carroClonado);
}
}
Diferença entre Shallow e Depp copy
A diferença entre cópias superficiais (“shallow copy”) e profundas (“deep copy”) é um conceito importante no padrão Prototype, assim como em outras áreas de programação onde a duplicação de objetos é necessária.
Shallow Copy (Cópia Superficial)
- Definição: Uma cópia superficial de um objeto copia todos os campos do objeto original para o novo objeto. Se os campos são valores primitivos, uma cópia direta dos valores é feita. Se os campos são referências a outros objetos, então as referências são copiadas, e não os objetos em si.
- Consequência: Após uma cópia superficial, os dois objetos (original e cópia) compartilharão as mesmas referências para objetos que não são primitivos. Qualquer modificação nesses objetos referenciados afetará ambos os objetos, o que pode ser indesejado em muitos cenários.
Deep Copy (Cópia Profunda)
- Definição: Uma cópia profunda copia todos os campos, e se um campo é uma referência a outro objeto, ele também copia o objeto referenciado e assim por diante. Isso resulta na duplicação completa não apenas do objeto original, mas também de todos os objetos aos quais ele se refere diretamente ou indiretamente.
- Consequência: Após uma cópia profunda, os dois objetos são completamente independentes. Modificar um objeto não afetará o outro. Isso é útil quando você deseja trabalhar com uma cópia verdadeiramente independente do objeto original.
Exemplo em Java
Vamos considerar um exemplo em Java para ilustrar esses conceitos:
class ObjetoReferenciado {
public int valor;
public ObjetoReferenciado(int valor) {
this.valor = valor;
}
}
class MeuObjeto implements Cloneable {
public int valorPrimitivo;
public ObjetoReferenciado objetoReferenciado;
public MeuObjeto(int valorPrimitivo, int valorReferenciado) {
this.valorPrimitivo = valorPrimitivo;
this.objetoReferenciado = new ObjetoReferenciado(valorReferenciado);
}
// Método para realizar uma cópia superficial
public MeuObjeto shallowCopy() throws CloneNotSupportedException {
return (MeuObjeto) this.clone();
}
// Método para realizar uma cópia profunda
public MeuObjeto deepCopy() {
MeuObjeto copia = new MeuObjeto(this.valorPrimitivo, this.objetoReferenciado.valor);
return copia;
}
}Neste exemplo, shallowCopy usa ==o método clone de Java, que por padrão realiza uma cópia superficial==. Isso significa que tanto o objeto original quanto a cópia compartilharão a mesma referência objetoReferenciado. Por outro lado, deepCopy cria uma nova instância de ObjetoReferenciado, garantindo que o objeto copiado tenha suas próprias cópias independentes de todos os objetos referenciados.
Escolher entre cópia superficial e profunda depende das necessidades específicas do seu programa e do comportamento que você deseja para os objetos copiados.