1 Definição
O padrão de protótipo é usar uma instância de classe para especificar o tipo de objeto a ser criado e criar novos objetos copiando esses protótipos. O padrão de protótipo requer que um objeto implemente uma interface que possa "clonar" a si mesmo, de modo que uma nova instância possa ser criada copiando o próprio objeto de instância.
diagrama de 2 classes
Classe de protótipo abstrato (Prototype): É a interface que declara o método clone e é a classe pai comum de todas as classes de protótipos concretas, podendo ser uma interface, uma classe abstrata ou mesmo uma classe de implementação concreta.
Classe de protótipo concreto (ConcretePrototype): Implementa o método clone declarado na classe protótipo abstrata e retorna um objeto clone próprio no método clone.
Classe cliente (Cliente): Na classe cliente, para usar o objeto protótipo, você só precisa criar ou diretamente NOVO (instanciar um) objeto protótipo através do método fábrica, e então você pode obter vários objetos idênticos através do método clone do objeto protótipo. Como o cliente é programado para objetos protótipos abstratos, ele também pode ser facilmente substituído por diferentes tipos de objetos protótipos!
3 Prós e contras do modo protótipo
Como forma de criar rapidamente um grande número de objetos idênticos ou semelhantes, o modo protótipo é amplamente utilizado no desenvolvimento de software.As operações CTRL+C e CTRL+V fornecidas por muitos softwares são aplicações típicas do modo protótipo!
3.1 Vantagens
Quando a instância do objeto criado é mais complexa, usar o padrão de protótipo pode simplificar o processo de criação do objeto!
Boa extensibilidade, porque a classe de protótipo abstrata é usada ao escrever o modo de protótipo, a classe de protótipo específica pode ser lida através da configuração quando o cliente está programando.
Você pode usar a cópia profunda para salvar o estado de um objeto e usar o modo de protótipo para copiar. Basta pular para quando você precisar se recuperar até um determinado ponto. Por exemplo, nossa espécie de ideia tem versões históricas, e o git também tem essas operações. funcionou perfeitamente!
3.2 Desvantagens
Cada classe precisa ser equipada com um método de cópia, e o método de cópia está localizado em uma classe. Quando a classe existente é modificada, o código-fonte precisa ser modificado, o que viola o princípio aberto-fechado.
Ao implementar a cópia profunda, é necessário escrever código mais complexo, e quando existem várias referências aninhadas entre objetos, para implementar a cópia profunda, as classes correspondentes a cada camada de objetos devem suportar a cópia profunda, o que é relativamente problemático de implementar .
4 cópia superficial e cópia profunda
Cópia superficial Cópia profunda é o conceito de cópia para tipos de referência. Ao usar uma instância de classe para chamar o método clone para gerar um novo objeto, o tipo de valor (oito tipos básicos, byte, short, int, long, char, double, float, boolean) é copiado diretamente. novo objeto muda, o antigo O objeto não muda com ele.
Para o tipo de objeto de referência, se for uma cópia superficial, a instância da nova classe e a instância da classe original são as mesmas para o tipo de objeto de referência, ou seja, se o valor do tipo de referência do novo objeto for alterado, o valor do tipo de referência do objeto antigo também será alterado. Uma cópia profunda é uma cópia completamente separada do objeto referenciado e não aponta mais para os objetos referenciados originais. As alterações no objeto referenciado pela nova instância não afetarão a instância de classe antiga.
O tipo String não é um tipo básico, é um tipo de referência, mas o tipo String é um tipo imutável. O valor para o qual ele aponta é constante. Alterar seu valor pelo objeto clonado na verdade altera o ponteiro do membro do tipo String do objeto clonado , que não afetará o valor do objeto clonado e seu ponteiro.
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
}
Por exemplo, agora existem duas classes, Professor e Aluno, e a classe Aluno é referenciada na classe Professor.
@Data
public class Teacher{
private int number;
private String name;
private Student student;
}
@Data
public class Student{
private String name;
}
public class client{
Student stu = new Student("zhangsan");
Teacher t = new Teacher(1, "lisi", stu);
}
A figura a seguir mostra a diferença entre tipos de valor e tipos de referência em cópias superficiais e profundas.
5 método de clone do Java
A classe Object do Java possui um método clone, que é protegido e precisa ser substituído para substituir o método clone. Todas as classes em Java herdam a classe Object por padrão, então todas as classes podem implementar o método clone.
protected native Object clone() throws CloneNotSupportedException;
O método clone() do Java faz uma cópia do objeto e o retorna ao chamador. Em geral, o método clone() satisfaz:
1. Para qualquer objeto x, existe x.clone() !=x//O objeto clonado não é o mesmo objeto que o objeto original
2. Para qualquer objeto x, existe x .clone().getClass()= =x.getClass(), ou seja, o objeto clonado é do mesmo tipo que o objeto original
3. Se o método equals() do objeto x estiver definido corretamente, então x .clone().equals(x) deve ser estabelecido.
A implementação do método clone é apenas uma delas, você também precisa herdar a interface Cloneable, caso contrário, uma CloneNotSupportedException será lançada.
package java.lang;
/**
* A class implements the <code>Cloneable</code> interface to
* indicate to the {@link java.lang.Object#clone()} method that it
* is legal for that method to make a
* field-for-field copy of instances of that class.
* <p>
* Invoking Object's clone method on an instance that does not implement the
* <code>Cloneable</code> interface results in the exception
* <code>CloneNotSupportedException</code> being thrown.
* <p>
* By convention, classes that implement this interface should override
* <tt>Object.clone</tt> (which is protected) with a public method.
* See {@link java.lang.Object#clone()} for details on overriding this
* method.
* <p>
* Note that this interface does <i>not</i> contain the <tt>clone</tt> method.
* Therefore, it is not possible to clone an object merely by virtue of the
* fact that it implements this interface. Even if the clone method is invoked
* reflectively, there is no guarantee that it will succeed.
*
* @author unascribed
* @see java.lang.CloneNotSupportedException
* @see java.lang.Object#clone()
* @since JDK1.0
*/
public interface Cloneable {
}
Os métodos básicos de clonagem de objetos em Java são os seguintes:
1. Para obter uma cópia do objeto, podemos utilizar o método clone() da classe Object.
2. Substitua o método clone() da classe base na classe derivada e declare-o como público.
3. No método clone() da classe derivada, chame super.clone().
4. Implemente a interface Cloneable na classe derivada.
6 Exemplos de Código
Monkey King, são 72 mudanças, quando Sun Wukong muda do protótipo para outra forma, é equivalente a uma cópia de si mesmo, que remete ao tipo de argola dourada.
6.1 protótipo Sun Wukong SunWuKong
@Data
public class SunWuKong implements Cloneable{
/**
* 表示孙悟空几号
*/
private int number;
/**
* 孙悟空技能描述
*/
private String desc;
/**
* 兵器尺寸
*/
private Staff staff;
/**
* 浅复制
* @return 孙悟空
* @throws CloneNotSupportedException 不支持复制异常
*/
@Override
public Object clone() throws CloneNotSupportedException {
SunWuKong sunWuKong = (SunWuKong)super.clone();
return sunWuKong;
}
}
6.2 Cajado do Aro Dourado
@Data
@AllArgsConstructor
public class Staff implements Cloneable{
/**
* 金箍棒的尺寸
*/
private int size;
}
6.3 Função principal MainClass
/**
* @program: design-pattern-learning
* @author: zgr
* @create: 2021-09-15 09:48
**/
public class MainClass {
public static void main(String[] args) {
try {
Staff staff = new Staff(200);
SunWuKong sunWuKong1 = new SunWuKong();
sunWuKong1.setNumber(1);
sunWuKong1.setDesc("我是1号");
sunWuKong1.setStaff(staff);
System.out.println(sunWuKong1);
System.out.println("*****************浅拷贝*******************");
SunWuKong sunWuKong2 = (SunWuKong) sunWuKong1.clone();
System.out.println(sunWuKong2);
System.out.println("*****************改变属性*******************");
staff.setSize(300);
sunWuKong2.setNumber(2);
sunWuKong2.setDesc("我原来是1号,我现在变了");
System.out.println(sunWuKong1);
System.out.println(sunWuKong2);
}catch (CloneNotSupportedException e){
System.out.println("对象不能被克隆");
}
}
}
6.4 Resultados em execução
6.5 Análise de resultados
Preste atenção no objeto de referência Staff, quando alteramos o atributo staff, o protótipo e o tipo de cópia de Sun Wukong foram alterados, o que significa que é uma cópia superficial.
7 Cópia profunda usando serialização Java
7.1 Código do protótipo
O código principal permanece o mesmo, basta adicionar a interface Serializable herdada à classe Staff e à classe SunWuKong e adicionar o método deepClone ao SunWuKong.
@Data
public class SunWuKong implements Cloneable, Serializable{
/**
* 表示孙悟空几号
*/
private int number;
/**
* 孙悟空技能描述
*/
private String desc;
/**
* 兵器尺寸
*/
private Staff staff;
/**
* 浅复制
* @return 孙悟空
* @throws CloneNotSupportedException 不支持复制异常
*/
@Override
public Object clone() throws CloneNotSupportedException {
SunWuKong sunWuKong = (SunWuKong)super.clone();
return sunWuKong;
}
public Object deepClone()throws IOException, ClassNotFoundException{
//将对象写到流里
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
//从流里读回来
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return ois.readObject();
}
}
@Data
@AllArgsConstructor
public class Staff implements Cloneable, Serializable {
/**
* 金箍棒的尺寸
*/
private int size;
}
7.2 Código de função principal
public class MainClass {
public static void main(String[] args) {
try {
Staff staff = new Staff(200);
SunWuKong sunWuKong1 = new SunWuKong();
sunWuKong1.setNumber(1);
sunWuKong1.setDesc("我是1号");
sunWuKong1.setStaff(staff);
System.out.println(sunWuKong1);
System.out.println("*****************浅拷贝*******************");
SunWuKong sunWuKong2 = (SunWuKong) sunWuKong1.clone();
staff.setSize(300);
sunWuKong2.setNumber(2);
sunWuKong2.setDesc("我原来是1号,我现在变2号");
System.out.println(sunWuKong1);
System.out.println(sunWuKong2);
System.out.println("*****************深拷贝*******************");
SunWuKong sunWuKong3 = (SunWuKong) sunWuKong1.deepClone();
staff.setSize(400);
sunWuKong3.setNumber(3);
sunWuKong3.setDesc("我是深拷贝3号");
System.out.println(sunWuKong1);
System.out.println(sunWuKong2);
System.out.println(sunWuKong3);
}catch (CloneNotSupportedException e){
System.out.println("对象不能被克隆");
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
7.3 Resultados corridos
7.4 Análise de resultados
Após a alteração do atributo tamanho da pauta, os atributos do nº 1 e nº 2 são alterados, e o nº 3 ainda é o atributo 300 antes da clonagem, o que significa que a referência ao objeto foi copiada novamente.
8 Cotações
1. "Padrões de Design Dahua"
3. Padrão de protótipo em java
4. Explique o método clone em Java em detalhes
9 Código fonte
https://github.com/airhonor/design-pattern-learning/tree/main/src/com/hz/design/pattern/prototype