欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

创建型——原型模式

程序员文章站 2022-06-13 15:25:06
...

创建型——原型模式

原型模式相对其他创建型的设计模式比较容易。其核心就是克隆方法。Object.clone()

定义

  • 原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

特点

  • 不需要知道任何创建细节,不调用构造函数。

适用场景

  • 类初始化需要消耗较多的资源

  • new产生的一个对象需要非常繁琐的过程(数据准备,访问权限等)

  • 构造函数比较复杂

  • 循环体中生产大量对象

优点

  • 性能上比new 一个对象高
  • 简化创建过程

缺点

  • 必须配备克隆方法。Object.clone()方法。
  • 对克隆复杂对象或者对克隆出的对象进行复杂改造的时候,会有风险。
  • 深拷贝,浅拷贝。

Code

业务场景

  • 邮件的创建。一封邮件包括:主题,收件人,抄送人,地址,内容,发送日期。邮件构建复杂
  • 循环发送邮件

业务分析与模型构建

  • 邮件的创建比较复杂,且需要循环进行发送,那么我们需要保存一份模板,然后动态的进行更改邮件里面的内容,最后循环发送。

  • Mail类进行构造邮件对象

  • MailUtil进行邮件的发送和模板记录

代码示例

  • Mail
import java.util.Date;

/**
 * 邮件对象
 */
public class Mail implements Cloneable{

    // 邮件主题
    private String topic;

    // 收件人
    private String addressee;

    // 抄送人列表
    private String ccList;

    // 邮箱地址
    private String emailAddress;

    //邮件内容
    private String content;

    // 发送日期
    private Date sendDate;

    public Mail() {
        // 此处假设构造方法及其复杂 ...
    }

    // ... 忽略getter setter

    // 克隆方法
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
  • MailUtil
/**
 * 邮件工具类,工具类一般要声明成final的
 */
public final class MailUtil {
    
    // 私有构造防止有外加new 对象的。
    private MailUtil(){}

    public static void sendMail(Mail mail){
        System.out.println("发送邮件成功!" + mail.getTopic());
    }

    public static void recordMailTemplate(Mail mail){
        System.out.println("mail模板信息:" + mail.getContent());
    }
}
  • TestMain测试
/**
 * 测试
 */
public class TestMain {

    public static void main(String[] args) throws CloneNotSupportedException {
        Mail mail = new Mail();
        mail.setContent("邮件模板内容");
        for (int i = 0; i < 10; i++) {
            Mail mailClone = (Mail) mail.clone();
            mailClone.setTopic("邮件测试" + i);
            mailClone.setAddressee(i + "");
            mailClone.setCcList(i + "");
            mailClone.setContent("邮件测试" + i);
            MailUtil.sendMail(mailClone);
        }
        // 这个保存模板的逻辑正常应该放在初始化模板之后
        // 在这里,为了刚方便演示原型模式,故意放在最后来保存邮件模板内容
        // 这个记录这一步骤里面,还会记录“邮件模板内容”几个字,而不是最后的“邮件测试9”
        MailUtil.recordMailTemplate(mail);

    }
}

深克隆,浅克隆——知识拓展

  • 深克隆:引用和对象的某些属性都进行了克隆。
  • 浅克隆:只克隆了引用,但是克隆的引用影响不了原来的引用持有的对象(读不懂这句话就运行一下下面的程序)

代码测试用例

  • Mail 重点关注clone()方法
package creational.prototype;

import java.util.Date;

/**
 * 邮件对象
 * @author zhongyuan.zhao
 * @date 2020-05-24 22:54
 */
public class Mail implements Cloneable{

    // 邮件主题
    private String topic;

    // 收件人
    private String addressee;

    // 抄送人列表
    private String ccList;

    // 邮箱地址
    private String emailAddress;

    //邮件内容
    private String content;

    // 发送日期
    private Date sendDate;

    public Mail() {
        // 此处假设构造方法及其复杂 ...
    }

    // 克隆方法
    @Override
    protected Object clone() throws CloneNotSupportedException {
        // 深克隆的写法,需要将指定的引用属性也进行克隆
        Mail mail = (Mail) super.clone();
        mail.sendDate = (Date) mail.sendDate.clone();
        return mail;
    }


    public Date getSendDate() {
        return sendDate;
    }

    public void setSendDate(Date sendDate) {
        this.sendDate = sendDate;
    }

    public String getTopic() {
        return topic;
    }

    public void setTopic(String topic) {
        this.topic = topic;
    }

    public String getAddressee() {
        return addressee;
    }

    public void setAddressee(String addressee) {
        this.addressee = addressee;
    }

    public String getCcList() {
        return ccList;
    }

    public void setCcList(String ccList) {
        this.ccList = ccList;
    }

    public String getEmailAddress() {
        return emailAddress;
    }

    public void setEmailAddress(String emailAddress) {
        this.emailAddress = emailAddress;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }
}
  • CloneTestMain
package creational.prototype;

import java.util.Date;

/**
 * 克隆的测试
 * @author zhongyuan.zhao
 * @date 2020-05-24 23:21
 */
public class CloneTestMain {

    public static void main(String[] args) throws CloneNotSupportedException {
        Mail mail = new Mail();
        mail.setSendDate(new Date());
        Mail mailClone = (Mail) mail.clone();

        System.out.println("=======初始状态======");
        System.out.println(mail.getSendDate());
        System.out.println(mailClone.getSendDate());

        System.out.println("=======改变克隆对象的时候,原对象属性不改变======");
        // 改变克隆对象的时候,原对象属性不改变
        mailClone.setSendDate(new Date(0L));
        System.out.println(mail.getSendDate());
        System.out.println(mailClone.getSendDate());

//        System.out.println("=======浅克隆的时候:改变原本对象,克隆对象会有改变,=======");
//        // 还原初始值
//        mailClone.setSendDate(mail.getSendDate());
//        // 改变原对象的时候,克隆对象会有改变
//        mail.setSendDate(new Date(0L));
//        System.out.println(mail.getSendDate());
//        System.out.println(mailClone.getSendDate());

        System.out.println("=======深克隆的时候:改变原本对象,克隆对象不会有改变,=======");

        mail.setSendDate(new Date(1000000000000000000L));
        System.out.println(mail.getSendDate());
        System.out.println(mailClone.getSendDate());

    }
}

源码阅读

实现了Cloneable接口的都重写了clone方法。有兴趣可以阅读一下。