原型模式

定义

  • 用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象
  • 这种模式存在的应用场景在于,能够复制当前对象,实现对象数据的克隆

需求

🐤批量发送邮件

实现方式 1

创建 Mail.java

/**
 * @author BNTang
 */
public class Mail {
    private String name;
    private String address;
    private String content;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public Mail() {
        System.out.println("创建邮件");
    }

    @Override
    public String toString() {
        return "Mail{" + "name='" + name + '\'' + ", address='" + address + '\'' + ", content='" + content + '\'' + '}';
    }
}

创建 MailUtil.java

/**
 * @author BNTang
 */
public class MailUtil {
    public static void sendMail(Mail mail) {
        String res = "收件人:" + mail.getName() + "邮件地址" + mail.getAddress() + "发送内容" + mail.getContent();
        System.out.println(res);
    }

    public static void saveOriginMail(Mail mail) {
        System.out.println("------------------");
        System.out.println(mail);
    }
}

创建 Client.java

/**
 * @author BNTang
 **/
public class Client {
    public static void main(String[] args) {
        Mail mail = new Mail();
        mail.setContent("原始模板内容");
        
        MailUtil.saveOriginMail(mail);

        for (int i = 1; i <= 10; i++) {
            mail.setName("姓名: 张三 " + i);
            mail.setAddress("地址: 北京 " + i);
            mail.setContent("内容: " + i);

            System.out.println(mail);
        }

        MailUtil.saveOriginMail(mail);
    }
}
  • 直接这样发送没有办法保存原始模板,最后得到的是最后一条邮件的内容

采用原型模式

  • 在 Mail 类中实现 cloneable 接口,覆盖 clone 方法

这个代码我就不贴了自行添加即可,其它修改的内容如下图,然后再次运行测试类代码

/**
 * @author BNTang
 **/
public class Client {
    public static void main(String[] args) throws Exception {
        Mail mail = new Mail();
        mail.setContent("原始模板内容");
        MailUtil.saveOriginMail(mail);

        for (int i = 1; i <= 10; i++) {
            Mail cloneMain = (Mail)mail.clone();
            cloneMain.setName("姓名: 张三 " + i);
            cloneMain.setAddress("地址: 北京 " + i);
            cloneMain.setContent("内容: " + i);

            System.out.println(cloneMain);
        }

        MailUtil.saveOriginMail(mail);
    }
}

实现方式 3

定义抽象原型

  • 创建 ProtoType.java 抽象类
/**
 * @author BNTang
 **/
public abstract class ProtoType implements Cloneable {
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

继承原型

直接运行测试类代码即可,效果如下图所示

ProtoType 类需要具备以下两个条件

实现 Cloneable 接口

  • 在 Java 语言有一个 Cloneable 接口,它的作用只有一个,就是在运行时通知虚拟机可以安全地在实现了此接口的类上使用 clone 方法
  • 在 Java 虚拟机中,只有实现了这个接口的类才可以被拷贝,否则在运行时会抛出 CloneNotSupportedException 异常

重写 Object 类中的 clone 方法

  • Java 中,所有类的父类都是 Object
  • Object 类中有一个 clone 方法,作用是返回对象的一个拷贝,但是其作用域是 protected 类型的,一般的类无法调用
  • 因此,ProtoType 类需要将 clone 方法的作用域修改为 public 类型

UML 图

深拷贝与浅拷贝

浅拷贝

  • 浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存

深拷贝

  • 创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址

🐤String 要不要深拷贝呢

原型实现深拷贝

  • 创建 Person.java

/**
 * @author BNTang
 */
public class Person implements Cloneable {
    private Integer age;
    private String name;
    private Date birthday;

    @Override
    public Object clone() throws CloneNotSupportedException {
        Person person = (Person)super.clone();

        // Date 是系统的类,不需要我们重写 clone 方法
        person.birthday = (Date)person.birthday.clone();
        return person;
    }

    @Override
    public String toString() {
        return "Person{" + "age=" + age + ", name='" + name + '\'' + ", birthday=" + birthday + '}';
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }
}
  • 创建 PersonClient.java
/**
 * @author BNTang
 **/
public class PersonClient {
    public static void main(String[] args) throws CloneNotSupportedException {
        Person person = new Person();
        person.setAge(10);
        person.setName("BNTang");
        person.setBirthday(new Date());

        System.out.println(person);

        Person clonePerson = (Person)person.clone();
        clonePerson.getBirthday().setTime(66666666666L);
        clonePerson.setName("JonathanLee");

        System.out.println(clonePerson);
        System.out.println(person);
    }
}

运行测试类代码,结果如下图所示

在源码中的应用

ArrayList

HashMap

Mybatis CacheKey

posted @   BNTang  阅读(31)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示