Padrões de Design - Padrões de Protótipo

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"

2. Padrão de protótipo JAVA

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

Acho que você gosta

Origin blog.csdn.net/honor_zhang/article/details/120412303
Recomendado
Clasificación