Java 24 Design Pattern 之 原型模式

原型模式介绍

概述

使用场景

UML类图

原型模式实现

浅拷贝模式

深拷贝模式


原型模式介绍

概述

原型模式顾名思义,就是基于原型来创建对象,用人话说就是一个对象的产生可以不由零起步,直接从一个已经具备一定雏形的对象克隆,然后再修改为所需要的对象。显而易见 ,原型模式属于创建型模式,

使用场景

如果对象的创建成本比较大,例如某个对象里面的数据需要访问数据库才能拿到;并且同一个类的不同对象之间差别不大(大部分字段都相同),这种场景下可以考虑使用原型模式达到提高程序性能的作用。

我们可以通过一个业务场景来理解原型模式的应用,设计一个学生类,学生类主要的成员变量有名字name,班级classId, needExtraCourse,以及所学的课程 course,其中course需要通过rpc调用查询课程系统获取,这个耗时100ms。如果用普通的方式为该类创建同一个班的4个 对象,如下所示:

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

class Student{
    private String name;
    private Integer classId;

    //是否需要额外多选课程 
    private Boolean needExtraCourse = false;
    
    //所学课程,需要通过rpc调用查询课程系统,耗时100ms
    private List<String> course = new ArrayList<>();

    public Student(String name, Integer classId) throws InterruptedException {
        this.name = name;
        this.classId = classId;

        //超时模拟rpc调用获取课程信息初始化course
        Thread.sleep(100);
        course.add("语文");
        course.add("数学");
        course.add("英语");
    }
    
    //getter、setter、toString函数省略
}

public class PrototypeExample {
    public static void main(String[] args) throws InterruptedException {
        long start =  new Date().getTime();
        Student student1 = new Student("张三",  1);
        Student student2 = new Student("李四",  1);
        Student student3 = new Student("王五",  1);
        Student student4 = new Student("赵六",  1);

        long end =  new Date().getTime();
        System.out.println("创建对象共花费了时间:" + (end -start) + " ms");
    }
}

******************【运行结果】******************
创建对象共花费了时间:413 ms

只是创建了4个对象就花费了400多ms,这样成本也太大了,其实对于一个班级的学生,不考虑额外选修课程的情况下(needExtraCourse = false),所学的课程应该是完全一样的。

因此可以用到今天介绍的原型模式:先用普通的方式创建一个对象,然后从创建的对象中克隆出其它对象,再修改其它对象的name字段即可。

UML类图

UML 类图也比较简单,只有两个部分:

  • • 1.Cloneable接口

  • • 2.Student类,实现了Cloneable接口的原型对象,这个对象有个能力就是可以克隆自己。

原型模式实现

原型模式的实现方式有两种:浅拷贝和深拷贝。关于浅拷贝和深拷贝,可以阅读详解浅拷贝与深拷贝

浅拷贝模式

浅拷贝仅仅复制所考虑的对象,而不复制它所引用的对象。Object类提供的方法clone只是拷贝本对象 , 其对象内部的数组、引用对象等都不拷贝,还是指向原生对象的内部元素地址,下面是浅拷贝原型模式实现的代码:

//1原型类需实现Cloneable接口
class Student implements Cloneable{
    //Student类的其余部分和上面例子一样
    ......................

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

public class PrototypeExample {
    public static void main(String[] args) throws InterruptedException, CloneNotSupportedException {
        long start =  new Date().getTime();
        Student student1 = new Student("张三",  1);

        Student student2 = (Student) student1.clone();
        student2.setName("李四");

        Student student3 = (Student) student1.clone();
        student3.setName("王五");

        Student student4 = (Student) student1.clone();
        student4.setName("赵六 ");

        System.out.println("student1--->" + student1);
        System.out.println("student2--->" + student2);
        System.out.println("student3--->" + student3);
        System.out.println("student4--->" + student4);

        long end =  new Date().getTime();
        System.out.println("创建对象共花费了时间:" + (end -start) + " ms");


        //赵六选修了课程美术
        student4.setNeedExtraCourse(true);
        student4.getCourse().add("美术 ");
        System.out.println("\n赵六选修美术课后:");
        System.out.println("student1--->" + student1);
        System.out.println("student2--->" + student2);
        System.out.println("student3--->" + student3);
        System.out.println("student4--->" + student4);
    }
}
******************【运行结果】******************
student1--->Student{name='张三', classId=1, needExtraCourse=false, course=[语文, 数学, 英语]}
student2--->Student{name='李四', classId=1, needExtraCourse=false, course=[语文, 数学, 英语]}
student3--->Student{name='王五', classId=1, needExtraCourse=false, course=[语文, 数学, 英语]}
student4--->Student{name='赵六 ', classId=1, needExtraCourse=false, course=[语文, 数学, 英语]}
创建对象共花费了时间:130 ms

赵六选修美术课后:
student1--->Student{name='张三', classId=1, needExtraCourse=false, course=[语文, 数学, 英语, 美术 ]}
student2--->Student{name='李四', classId=1, needExtraCourse=false, course=[语文, 数学, 英语, 美术 ]}
student3--->Student{name='王五', classId=1, needExtraCourse=false, course=[语文, 数学, 英语, 美术 ]}
student4--->Student{name='赵六 ', classId=1, needExtraCourse=true, course=[语文, 数学, 英语, 美术 ]}

        可以看到,通过原型模式的方式,同样是创建四个对象,只花了100多ms,大大的提高了程序性能。

        但是这里还存在一个小问题:赵六比较好学,所以还选修了美术,但是通过程序的运行结果可以看到,明明只是赵六选修了美术,但是其他三个同学的课表中也都出现了美术课 。这是因为浅拷贝虽然产生了两个完全不同的对象,但是对象中有对其他对象的引用(如这里的List)都指向同一个对象。

为了解决这个问题,我们引入了深拷贝模式。

深拷贝模式

深拷贝模式把要复制的对象所引用的对象都拷贝了一遍。

class Student implements Cloneable{
    //Student类的其余部分和上面例子一样
    ......................
    @Override
    public Object clone() throws CloneNotSupportedException {
        Object object = super.clone();
        Student student = (Student)object;

        List<String> newCourse = new ArrayList<>();
        Iterator<String>  it = student.course.iterator();

        while (it.hasNext()) {
            newCourse.add(it.next());
        }

        student.course  = newCourse;
        return object;
    }
}

public class PrototypeExample {
    public static void main(String[] args) throws InterruptedException, CloneNotSupportedException {
        long start =  new Date().getTime();
        Student student1 = new Student("张三",  1);

        Student student2 = (Student) student1.clone();
        student2.setName("李四");

        Student student3 = (Student) student1.clone();
        student3.setName("王五");

        Student student4 = (Student) student1.clone();
        student4.setName("赵六 ");

        System.out.println("student1--->" + student1);
        System.out.println("student2--->" + student2);
        System.out.println("student3--->" + student3);
        System.out.println("student4--->" + student4);

        long end =  new Date().getTime();
        System.out.println("创建对象共花费了时间:" + (end -start) + " ms");


        //赵六选修了课程美术
        student4.setNeedExtraCourse(true);
        student4.getCourse().add("美术 ");
        System.out.println("\n赵六选修美术课后:");
        System.out.println("student1--->" + student1);
        System.out.println("student2--->" + student2);
        System.out.println("student3--->" + student3);
        System.out.println("student4--->" + student4);
    }
}

******************【运行结果】******************
student1--->Student{name='张三', classId=1, needExtraCourse=false, course=[语文, 数学, 英语]}
student2--->Student{name='李四', classId=1, needExtraCourse=false, course=[语文, 数学, 英语]}
student3--->Student{name='王五', classId=1, needExtraCourse=false, course=[语文, 数学, 英语]}
student4--->Student{name='赵六 ', classId=1, needExtraCourse=false, course=[语文, 数学, 英语]}
创建对象共花费了时间:135 ms

赵六选修美术课后:
student1--->Student{name='张三', classId=1, needExtraCourse=false, course=[语文, 数学, 英语]}
student2--->Student{name='李四', classId=1, needExtraCourse=false, course=[语文, 数学, 英语]}
student3--->Student{name='王五', classId=1, needExtraCourse=false, course=[语文, 数学, 英语]}
student4--->Student{name='赵六 ', classId=1, needExtraCourse=true, course=[语文, 数学, 英语, 美术 ]}

可以看到,深拷贝模式 ,修改一个对象的引用类型的成员不会再影响另外对象的该成员了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

time Friend

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值