【设计模式】原型模式

文章目录


前言

【设计模式】原型模式:用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型对象相同的新对象。


一、原型模式

  • 概念:用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型对象相同的新对象。
  • 原型模式包含如下角色:
    • 抽象原型类:规定了具体原型对象必须实现的的 clone() 方法。
    • 具体原型类:实现抽象原型类的 clone() 方法,它是可被复制的对象。
    • 访问类:使用具体原型类中的 clone() 方法来复制新的对象。

1.实现

原型模式的克隆分为浅克隆和深克隆。

  • 浅克隆:创建一个新对象,新对象的属性和原来对象完全相同 ,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址
  • 深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址

浅克隆

Java中的Object类中提供了 clone() 方法来实现浅克隆 。 Cloneable 接口是下面的类图中的抽象原型类,而实现了Cloneable接口的子实现类 就是具体的原型类。代码如下:

接口类图如下:

Realizetype类:

java 复制代码
public class Realizetype implements Cloneable {

    public Realizetype() {
        System.out.println("具体的原型对象创建完成!");
    }

    @Override
    protected Realizetype clone() throws CloneNotSupportedException {
        System.out.println("具体原型复制成功!");
        return (Realizetype) super.clone();
    }
}

PrototypeTest(测试访问类):

java 复制代码
public class PrototypeTest {
    public static void main(String[] args) throws CloneNotSupportedException {
        Realizetype r1 = new Realizetype();
        Realizetype r2 = r1.clone();

        System.out.println("对象r1和r2是同一个对象?" + (r1 == r2));
    }
}

案例

用原型模式生成"三好学生"奖状

同一学校的"三好学生"奖状除了获奖人姓名不同,其他都相同,可以使用原型模式复制多个"三好学生"奖状出来,然后在修改奖状上的名字即可。

类图如下:

java 复制代码
//奖状类
public class Citation implements Cloneable {
    private String name;

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

    public String getName() {
        return (this.name);
    }

    public void show() {
        System.out.println(name + "同学:在2020学年第一学期中表现优秀,被评为三好学生。特发此状!");
    }

    @Override
    public Citation clone() throws CloneNotSupportedException {
        return (Citation) super.clone();
    }
}

//测试访问类
public class CitationTest {
    public static void main(String[] args) throws CloneNotSupportedException {
        Citation c1 = new Citation();
        c1.setName("张三");

        //复制奖状
        Citation c2 = c1.clone();
        //将奖状的名字修改李四
        c2.setName("李四");

        c1.show();
        c2.show();
    }
}

深克隆

上面的案例扩展:将上面的"三好学生"奖状的案例中Citation类的name属性修改为Student类型的属性。代码如下:

java 复制代码
//奖状类
public class Citation implements Cloneable {
    private Student stu;

    public Student getStu() {
        return stu;
    }

    public void setStu(Student stu) {
        this.stu = stu;
    }

    void show() {
        System.out.println(stu.getName() + "同学:在2020学年第一学期中表现优秀,被评为三好学生。特发此状!");
    }

    @Override
    public Citation clone() throws CloneNotSupportedException {
        return (Citation) super.clone();
    }
}

//学生类
public class Student {
    private String name;
    private String address;

    public Student(String name, String address) {
        this.name = name;
        this.address = address;
    }

    public Student() {
    }

    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 class CitationTest {
    public static void main(String[] args) throws CloneNotSupportedException {

        Citation c1 = new Citation();
        Student stu = new Student("张三", "西安");
        c1.setStu(stu);

        //复制奖状
        Citation c2 = c1.clone();
        //获取c2奖状所属学生对象
        Student stu1 = c2.getStu();
        stu1.setName("李四");

        //判断stu对象和stu1对象是否是同一个对象
        System.out.println("stu和stu1是同一个对象?" + (stu == stu1));

        c1.show();
        c2.show();
    }
}

分析:​ 创建一个新的奖状对象,然后克隆这个奖状对象,获取并修改克隆对象内的成员stu,结果发现两个奖状的成员都变成了修改后的,说明浅克隆是引用克隆,引用属性指向的是同一块地址。stu对象和stu1对象是同一个对象,就会产生将stu1对象中name属性值改为"李四",两个Citation(奖状)对象中显示的都是李四。这就是浅克隆的效果,对具体原型类(Citation)中的引用类型的属性进行引用的复制 。这种情况需要使用深克隆,而进行深克隆需要使用对象流

深克隆(其他类都要实现Serializable接口,因为要序列化;其他不变):

java 复制代码
public class ClitationTest {
    public static void main(String[] args) throws CloneNotSupportedException, IOException, ClassNotFoundException {
        Clitation clitation = new Clitation();
        Student student = new Student();
        student.setName("张三");
        clitation.setStu(student);

        //创建对象输出流对象
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\\Users\\ThundeRobot\\Desktop\\b.txt"));
        //将c1对象写出到文件中
        oos.writeObject(clitation);
        oos.close();

        //创建对象出入流对象
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:\\Users\\ThundeRobot\\Desktop\\b.txt"));
        //读取对象
        Clitation clone = (Clitation) ois.readObject();
        //获取c2奖状所属学生对象
        Student student1 = clone.getStu();
        student1.setName("李四");

        //判断stu对象和stu1对象是否是同一个对象
        System.out.println("stu和stu1是同一个对象?" + (student == student1));

        clitation.show();
        clone.show();
    }
}

2.使用场景

  • 对象的创建非常复杂,可以使用原型模式快捷的创建对象。
  • 性能和安全要求比较高。

二、细究总结

1.何乐而不为

  • 平常实例化对象时每new 一次,都需要执行 一次构造函数 ,如果构造函数的执行时间很长,那么多次执行这个初始化操作就实在太低效了。一般在初始化的信息不发生变化的情况下"克隆星最好的办法 "这既隐藏了对象创建的细节"又对性能是大大的提高"何乐而不为呢?
  • 它等于是不用重新初始化对象而是动态地获得对象运行时的状态

2."bug"出在引用类型

  • 而String是一种拥有值类型特点的特殊引用类型,supe.clone()方法是这样,如果字段是值类型的,则对该字段执行逐位复制 ,如果字段是引用类型,则复制引用但不复制引用的对象 :因此原始对象及真副本引用同一对象
  • 浅复制:被复制对象的所有变量都含有与原来的对象相同的值 ,而所有的对其他对象的引用都仍然指向原来的对象
  • 深复制:把引用对象的变量指向复制过的新对象,而不是原有的被引用的对象。
  • java引用类型:接口数组

总结

以上就是设计模式------原型模式的讲解。

相关推荐
喂完待续7 分钟前
【序列晋升】29 Spring Cloud Task 微服务架构下的轻量级任务调度框架
java·spring·spring cloud·云原生·架构·big data·序列晋升
benben04410 分钟前
ReAct模式解读
java·ai
烛阴42 分钟前
【TS 设计模式完全指南】从“入门”到“劝退”,彻底搞懂单例模式
javascript·设计模式·typescript
轮到我狗叫了1 小时前
牛客.小红的子串牛客.kotori和抽卡牛客.循环汉诺塔牛客.ruby和薯条
java·开发语言·算法
OC溥哥9992 小时前
Flask论坛与个人中心页面开发教程完整详细版
后端·python·flask·html
Volunteer Technology2 小时前
三高项目-缓存设计
java·spring·缓存·高并发·高可用·高数据量
栗子~~2 小时前
bat脚本- 将jar 包批量安装到 Maven 本地仓库
java·maven·jar
Meteors.2 小时前
23种设计模式——原型模式 (Prototype Pattern)详解
设计模式·原型模式
Mr.Entropy3 小时前
ecplise配置maven插件
java·maven
叙白冲冲3 小时前
tomcat 为啥能一直运行?不像方法那样结束?
java·tomcat