[Uso de Java] Patrón de diseño de Java (tres) patrón de prototipo (Patrón de prototipo)

Tabla de contenido

1. Introducción al modo de prototipo

1.1 Introducción al modo prototipo

1.2, el papel del modo prototipo

1.3 Ventajas y desventajas del modo prototipo

1.4. Escenarios de uso del modo prototipo

1.5, copia superficial y copia profunda

En segundo lugar, la implementación del modo prototipo

2.1 Cliente de código

2.2 Código de copia superficial

2.3 Código de copia profunda

Tres, el análisis del código original


1. Introducción al modo de prototipo

1.1 Introducción al modo prototipo

El modo prototipo utiliza la instancia del prototipo para especificar el tipo de objeto que se creará y crea un nuevo objeto copiando el objeto prototipo.

El modo de prototipo proporciona una interfaz (clon) para crear nuevos objetos a través de objetos existentes. La implementación de clone () está relacionada con el lenguaje específico y se implementa en C ++ a través del constructor de copias.

El modo de prototipo consiste en crear otro objeto personalizable a partir de un objeto sin conocer ningún detalle de creación. En el caso de que la información inicializada no cambie, la clonación es el mejor método, que no solo oculta los detalles de la creación del objeto, sino que también mejora enormemente el rendimiento. Porque si no usas clone, necesitas ejecutar el constructor una vez por cada nuevo, si el tiempo de ejecución del constructor es muy largo, es demasiado ineficiente para realizar la operación de inicialización varias veces.

El modo de prototipo debe usar una copia profunda al implementar la interfaz de clonación.

El modo de prototipo se enfoca en crear nuevos objetos de clase a partir de la autoasignación, ocultando los detalles de la creación.

Este modo que no usa la palabra clave new para generar un objeto, sino que lo logra a través de la copia del objeto, se llama modo prototipo. El diagrama de clases general se muestra arriba.

1.2, el papel del modo prototipo

Rol de prototipo abstracto (Prototipo): especifica la interfaz que debe implementar el objeto prototipo concreto (si se va a proporcionar una copia profunda, debe haber disposiciones para implementar el clon).

Prototipo Concreto: Derivado del prototipo abstracto, es el objeto utilizado por el programa cliente, es decir, el objeto a copiar, que necesita implementar la interfaz requerida por el rol de prototipo abstracto.

Rol de cliente (Client): el programa cliente que utiliza el objeto prototipo.

1.3 Ventajas y desventajas del modo prototipo

ventaja:

A. El modelo prototipo oculta categorías de productos específicas a los clientes.

B. Agregar y eliminar productos en tiempo de ejecución: el modo de prototipo permite que una nueva categoría de producto específica se incorpore al sistema solo a través del registro del cliente de la instancia del prototipo.

C. Cambiar valores para especificar nuevos objetos: un sistema altamente dinámico permite definir nuevos comportamientos a través de la composición de objetos. Por ejemplo, asignando un valor a una variable de objeto y no definiendo una nueva clase. Al crear una instancia de una clase existente y registrar la instancia como un prototipo de un objeto cliente, puede definir efectivamente una nueva clase de objeto. Los clientes pueden delegar responsabilidades a los prototipos para mostrar nuevos comportamientos.

D. Cambiar la estructura para especificar nuevos objetos: muchas aplicaciones crean objetos a partir de componentes y subcomponentes.
E. Reducir la construcción de subclases. El modo de prototipo clona un prototipo en lugar de solicitar un método de fábrica para generar un nuevo objeto.

F. Usar clases para configurar aplicaciones dinámicamente Algunos entornos de ejecución permiten que las clases se carguen dinámicamente en las aplicaciones.

G. Usar el modo prototipo para crear un objeto es mucho mejor en términos de rendimiento que un objeto directamente nuevo, porque el método de clonación de la clase Object es un método local que manipula directamente el flujo binario en la memoria, especialmente al copiar objetos grandes. la diferencia de rendimiento es muy obvia.

H. Otra ventaja de utilizar el modo prototipo es que simplifica la creación de objetos, lo que facilita la creación de objetos.

Desventajas:

El principal inconveniente del modo prototipo es que cada subclase del prototipo abstracto Prototipo debe implementar la operación de clonación, y puede ser difícil implementar la función de clonación. Cuando la clase considerada ya existe, es difícil agregar una operación de clonación. Cuando el interno incluye algunos objetos que no admiten copia o tienen referencias circulares, también puede ser difícil implementar la clonación.

1.4. Escenarios de uso del modo prototipo

La idea principal del modo prototipo es clonar un nuevo objeto basado en un objeto existente. Generalmente, se proporciona un método de clonación dentro del objeto y se devuelve una copia del objeto a través del método de clonación.

Escenarios de uso del modo de prototipo:

R. La inicialización de clases necesita digerir una gran cantidad de recursos, este recurso incluye datos, recursos de hardware, etc .;

B. Generar un objeto a través de nuevo requiere una preparación de datos muy tediosa o permisos de acceso, por lo que puede utilizar el modo de prototipo;

C. Cuando es necesario proporcionar un objeto a otros objetos para su acceso, y cada persona que llama puede necesitar modificar su valor, considere usar el modo de prototipo para copiar varios objetos para que los use la persona que llama;

En proyectos reales, el modo de prototipo rara vez aparece solo y, por lo general, aparece junto con el modo de método de fábrica. Se crea un objeto mediante el método de clonación y, a continuación, se proporciona el método de fábrica al llamador.

1.5, copia superficial y copia profunda

Una copia superficial

Todas las variables del objeto copiado contienen el mismo valor que el objeto original y las referencias a otros objetos aún apuntan al objeto original. Es decir, la copia superficial solo es responsable de la instancia del objeto actual y no copia el objeto al que se hace referencia.

B, copia profunda

Todas las variables del objeto copiado contienen el mismo valor que el objeto original, excepto las variables que hacen referencia a otros objetos. Las variables que hacen referencia a otros objetos señalarán a un nuevo objeto que se está copiando en lugar del objeto de referencia original. Es decir, la copia profunda copia todos los objetos a los que hace referencia el objeto que se va a copiar.

Qué tan profundo debe ser una copia profunda es una pregunta incierta. Al decidir copiar un objeto en el modo de copia profunda, debe decidir si usar copia superficial, copia profunda o continuar usando copia profunda para el objeto copiado indirectamente. Por lo tanto, al tomar una copia profunda, debe decidir qué tan profunda es. Además, en el proceso de copia profunda, es probable que se produzca el problema de las referencias circulares.
    

En segundo lugar, la implementación del modo prototipo

Los ejemplos de implementación de código de todos los patrones de diseño se pueden ver en Code Cloud. Si está interesado, puede verificarlo. Dirección de Code Cloud: https://gitee.com/no8g/java-design-patterns

2.1 Cliente de código

public class Client {

    /**
     * 发送账单的数量,这个值是从数据库中获得
     */
    public static final int MAX_COUNT = 10000;

    public static void main(String[] args) {
        // 模拟发送邮件
        int i = 0;

        // 把模板定义出来,这个是从数据库中获得
        Mail mail = new Mail(new AdvTemplate());
        mail.setTail("XX银行版权所有");
        long startTime = System.currentTimeMillis();
        while (i < MAX_COUNT) {
            // 以下是每封邮件不同的地方
            Mail cloneMail = mail.clone();
            cloneMail.setAppellation(getRandString(5) + "先生(女士)");
            cloneMail.setReceiver(getRandString(8) + "@" + getRandString(5) + ".com");

            // 然后发送邮件
            sendMail(cloneMail);
            i++;
        }

        System.out.println("总共耗时: " + (System.currentTimeMillis() - startTime) + "毫秒");
    }

    public static void sendMail(Mail mail) {
        System.out.println("标题:" + mail.getSubject() + "\t收件人:" + mail.getReceiver() + "\t .....发送成功!");
    }

    /**
     * 获得指定长度的随机字符串
     *
     * @return 随机字符串
     */
    public static String getRandString(int maxLength) {

        String source ="abcdefghijklmnopqrskuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        StringBuilder sb = new StringBuilder();
        Random random = new Random();
        for (int i = 0; i < maxLength; i++) {
            sb.append(source.charAt(random.nextInt(source.length())));
        }

        return sb.toString();
    }
}
package com.iot.practice.designpattern.prototype;

/**
 * <p>AdvTemplate 此类用于:</p>
 * <p>@author:hujm</p>
 * <p>@date:2021年02月06日 10:01</p>
 * <p>@remark:在类图中 AdvTemplate 是广告信的模板,一般都是从数据库取出,生成一个 BO 或者是 DTO,我们这里
 * 使用一个静态的值来做代表;Mail 类是一封邮件类,发送机发送的就是这个类,我们先来看看我们的程序:</p>
 */
public class AdvTemplate {

    /**
     * 广告信名称
     */
    private String advSubject = "XX银行国庆信用卡抽奖活动";

    /**
     * 广告信内容
     */
    private String advContent = "国庆抽奖活动通知:只要刷卡就送你1百万!....";

    /**
     * 取得广告信的名称
     *
     * @return 广告信的名称
     */
    public String getAdvSubject() {
        return this.advSubject;
    }

    /**
     * 取得广告信的内容
     *
     * @return 广告信的内容
     */
    public String getAdvContent() {
        return this.advContent;
    }
}
package com.iot.practice.designpattern.prototype;

import lombok.Data;

/**
 * <p>Mail 此类用于:</p>
 * <p>@author:hujm</p>
 * <p>@date:2021年02月06日 10:05</p>
 * <p>@remark:Mail 就是一个业务对象,我们再来看业务场景类是怎么调用的:</p>
 */
@Data
public class Mail implements Cloneable {

    /**
     * 收件人
     */
    private String receiver;

    /**
     * 邮件名称
     */
    private String subject;

    /**
     * 称谓
     */
    private String appellation;

    /**
     * 邮件内容
     */
    private String context;

    /**
     * 邮件的尾部,一般都是加上“XXX版权所有”等信息
     */
    private String tail;

    /**
     * 构造函数
     */
    public Mail(AdvTemplate advTemplate) {
        this.context = advTemplate.getAdvContent();
        this.subject = advTemplate.getAdvSubject();
    }

    /**
     * 增加了一个 Cloneable 接口, Mail 实现了这个接口,在 Mail 类中重写了 clone()方法,我们来看 Mail类的改变:
     *
     * 在 clone()方法上增加了一个注解@Override,没有继承一个类为什么可以重写呢?在 Java 中所有类的
     * 老祖宗是谁?对嘛,Object 类,每个类默认都是继承了这个类,所以这个用上重写是非常正确的。
     *
     * @return Mail
     */
    @Override
    public Mail clone() {

        Mail mail = null;
        try {
            mail = (Mail) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }

        return mail;
    }
}

2.2 Código de copia superficial

package com.iot.practice.designpattern.prototype.shallowcopy;

/**
 * <p>ShallowCopyClient 此类用于:</p>
 * <p>@author:hujm</p>
 * <p>@date:2021年02月06日 14:57</p>
 * <p>@remark:
 * 在 Thing 类中增加一个私有变量 arrayLis,类型为 ArrayList,然后通过 setValue 和 getValue 分别进
 * 行设置和取值,我们来看场景类:
 * </p>
 */
public class ShallowCopyClient {

    public static void main(String[] args) {

        // 产生一个对象
        ShallowCopy shallowCopy = new ShallowCopy();
        // 设置一个值
        shallowCopy.setValue("张三");

        // 拷贝一个对象
        ShallowCopy cloneShallowCopy = shallowCopy.clone();
        cloneShallowCopy.setValue("李四");

        // 运行的结果是:[张三, 李四]
        System.out.println(shallowCopy.getValue());

        /**
         * 怎么会有李四呢?是因为 Java 做了一个偷懒的拷贝动作,Object 类提供的方法 clone() 只是拷贝本对象,
         * 其对象内部的数组、引用对象等都不拷贝,还是指向原生对象的内部元素地址,这种拷贝就叫做浅拷贝,
         * 确实是非常浅,两个对象共享了一个私有变量,你改我改大家都能改,是一个种非常不安全的方式,在实
         * 际项目中使用还是比较少的。你可能会比较奇怪,为什么在 Mail 那个类中就可以使用使用 String 类型,
         * 而不会产生由浅拷贝带来的问题呢?内部的数组和引用对象才不拷贝,其他的原始类型比如
         * int,long,String(Java 就希望你把 String 认为是基本类型,String 是没有 clone() 方法的)等都会被拷贝的。
         */
    }
}
package com.iot.practice.designpattern.prototype.shallowcopy;

import java.util.ArrayList;

/**
 * <p>ShallowCopy 此类用于:</p>
 * <p>@author:hujm</p>
 * <p>@date:2021年02月06日 14:52</p>
 * <p>@remark:</p>
 */
public class ShallowCopy implements Cloneable {

    /**
     * 定义一个私有变量
     */
    private ArrayList<String> arrayList = new ArrayList<>();

    @Override
    public ShallowCopy clone() {

        ShallowCopy shallowCopy = null;

        try {
            shallowCopy = (ShallowCopy) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }

        return shallowCopy;
    }

    /**
     * 取得arrayList的值
     */
    public ArrayList<String> getValue() {
        return this.arrayList;
    }

    /**
     * 设置List的值
     *
     * @param value 值
     */
    public void setValue(String value) {
        this.arrayList.add(value);
    }
}

2.3 Código de copia profunda

package com.iot.practice.designpattern.prototype.deepcopy;

/**
 * <p>ShallowCopyClient 此类用于:</p>
 * <p>@author:hujm</p>
 * <p>@date:2021年02月06日 14:57</p>
 * <p>@remark:
 * 在 Thing 类中增加一个私有变量 arrayLis,类型为 ArrayList,然后通过 setValue 和 getValue 分别进
 * 行设置和取值,我们来看场景类:
 * </p>
 */
public class DeepCopyClient {

    public static void main(String[] args) {

        // 产生一个对象
        DeepCopy deepCopy = new DeepCopy();
        // 设置一个值
        deepCopy.setValue("张三");

        // 拷贝一个对象
        DeepCopy cloneDeepCopy = deepCopy.clone();
        cloneDeepCopy.setValue("李四");

        // 运行的结果是:[张三]
        System.out.println(deepCopy.getValue());

        /**
         * 这个实现了完全的拷贝,两个对象之间没有任何的瓜葛了,你修改你的,我修改我的,不相互影响,
         * 这种拷贝就叫做深拷贝,深拷贝还有一种实现方式就是通过自己写二进制流来操作对象,然后实现对象的
         * 深拷贝,这个大家有时间自己实现一下。
         * 深拷贝和浅拷贝建议不要混合使用,一个类中某些引用使用深拷贝某些引用使用浅拷贝,这是一种非
         * 常差的设计,特别是是在涉及到类的继承,父类有几个引用的情况就非常的复杂,建议的方案深拷贝和浅
         * 拷贝分开实现
         */
    }
}
package com.iot.practice.designpattern.prototype.deepcopy;

import java.util.ArrayList;

/**
 * <p>DeepCopy 此类用于:</p>
 * <p>@author:hujm</p>
 * <p>@date:2021年02月06日 15:03</p>
 * <p>@remark:</p>
 */
public class DeepCopy implements Cloneable {

    /**
     * 定义一个私有变量
     */
    private ArrayList<String> arrayList = new ArrayList<>();

    @Override
    public DeepCopy clone() {
        DeepCopy deepCopy = null;

        try {
            deepCopy = (DeepCopy) super.clone();
            this.arrayList = (ArrayList<String>) this.arrayList.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }

        return deepCopy;
    }

    /**
     * 设置值
     */
    public void setValue(String value) {
        this.arrayList.add(value);
    }

    /**
     * 取值
     */
    public ArrayList<String> getValue() {
        return this.arrayList;
    }
}

Tres, el análisis del código original

 

¡fin!

Supongo que te gusta

Origin blog.csdn.net/weixin_44299027/article/details/113717939
Recomendado
Clasificación