设计模式是一套被反复使用,多数人知晓,经过分类编目的,代码设计的总结,也可以说是前人的智慧结晶。学习设计模式能让我们对一些应用场景使用相同的套路达到很好的效果,我会不定时更新一些自己对设计模式的理解的文章,
从定义,实现,应用场景来说说设计模式,今天我要说的对象是原型模式
一:原理
原型模式是一种创建型设计模式,Prototype模式允许一个对象再创建另外一个可定制的对象,根本无需知道任何如何创建的细节,工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动
创建的对象通过请求原型对象拷贝它们自己来实施创建。
通俗的说,原型模式就是实现对对象复制的过程,将对象的所有属性复制给一个新的对象,这两个对象对应的内存地址不一样,因为新的对象是new出来的,但是所有的属性都一样,因为新对象的所有属性
都是从老对象那里复制的
二:实现
1.先看一个原型模式的简单实现
//创建一个简单的学生类,用户演示
-
-
-
-
- class Student{
-
-
-
-
- private String name;//姓名
- private String stuNO;//学号
- private String grade;//班级
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- public String getStuNO() {
- return stuNO;
- }
-
- public void setStuNO(String stuNO) {
- this.stuNO = stuNO;
- }
-
- public String getGrade() {
- return grade;
- }
-
- public void setGrade(String grade) {
- this.grade = grade;
- }
-
- public Student() {
-
- }
-
- public Student(String name, String stuNO, String grade) {
- this.name = name;
- this.stuNO = stuNO;
- this.grade = grade;
- }
-
- public Student clone(){
- Student student=new Student();
- student.setGrade(this.grade);
- student.setName(this.name);
- student.setStuNO(this.stuNO);
- return student;
- }
- //重写toString方法是为了演示
- @Override
- public String toString() {
- return "Student{" +
- "name='" + name + '\'' +
- ", stuNO='" + stuNO + '\'' +
- ", grade='" + grade + '\'' +
- '}';
- }
-
- public static void main(String[] fd){
- Student student=new Student("游坦之","11094","金庸群侠");
- Student newStudent=student.clone();
- System.out.println(newStudent);
- }
-
- }
打印结果:Student{name='游坦之', stuNO='11094', grade='金庸群侠'}
对于java语言来了,每个对象都可以继承来自Object的clone()方法,这个方法刚好可以创建一个对象的副本实现原型模式。不过有个问题需要注意,类的成员变量有两种类型,一种是基本数据类型像byte,short,int等,,还有一种是
引用对象,如类,数组,接口等,而clone()方法,只会把基本数据类型的值复制给对方,而引用数据类型的只会吧引用的地址复制过去,所以对于引用数据类型的成员变量来说,还是使用的同一个实例。当然,要把引用数据类型的成员
变量也new过去也是有办法的,而鉴于能不能对引用数据类型实现new,把原型模式的实现分为浅克隆和深克隆,下面介绍这两种方式
2.浅克隆
浅克隆的实现非常简单,只需要将上面简单实现的clone方法改成Object的clone()方法就可以了,不过被克隆的对象需要实现Cloneable接口,这个接口不需要实现任何的方法,只是一个允许被克隆的标准,如果没有实现这个接口,
使用clone()方法的时候会抛出CloneNotSupportedException异常。Cloneable接口的意义和Serializable非常相似,只是功能不一样。下面是更改过后的Student类
- class Student implements Cloneable{
- private String name;//姓名
- private String stuNO;//学号
- private String grade;//班级
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public String getStuNO() {
- return stuNO;
- }
- public void setStuNO(String stuNO) {
- this.stuNO = stuNO;
- }
- public String getGrade() {
- return grade;
- }
- public void setGrade(String grade) {
- this.grade = grade;
- }
- public Student() {
- }
- public Student(String name, String stuNO, String grade) {
- this.name = name;
- this.stuNO = stuNO;
- this.grade = grade;
- }
- public Student simpleClone()throws Exception{
- return (Student)super.clone();
- }
- //重写toString方法是为了演示
- @Override
- public String toString() {
- return "Student{" +
- "name='" + name + '\'' +
- ", stuNO='" + stuNO + '\'' +
- ", grade='" + grade + '\'' +
- '}';
- }
- public static void main(String[] fd){
- Student student=new Student("游坦之","11094","金庸群侠");
- Student newStudent= null;
- try {
- newStudent = student.simpleClone();
- } catch (Exception e) {
- e.printStackTrace();
- }
- System.out.println(newStudent);
- }
- }打印的结果:Student{name='游坦之', stuNO='11094', grade='金庸群侠'}
3.深克隆
深克隆,也就是要对应用类型的属性,甚至文件,也实现克隆,也就是保存对象即时的所有形式,可以通过java的序列化方法实现。这里在原本的学生类型增加Identity属性,更改后代码如下
- class Identity implements Serializable{
- private String identityNo;//身份身份证号码
- private String address;//家庭住址
- private String houseNumber;//门牌号
- public Identity() {
- }
- public Identity(String identityNo, String address, String houseNumber) {
- this.identityNo = identityNo;
- this.address = address;
- this.houseNumber = houseNumber;
- }
- //重写toString方法,方便演示
- @Override
- public String toString() {
- return "Identity{" +
- "identityNo='" + identityNo + '\'' +
- ", address='" + address + '\'' +
- ", houseNumber='" + houseNumber + '\'' +
- '}';
- }
- }
- class Student implements Serializable{
- private String name;//姓名
- private String stuNO;//学号
- private String grade;//班级
- private Identity identity;
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public String getStuNO() {
- return stuNO;
- }
- public void setStuNO(String stuNO) {
- this.stuNO = stuNO;
- }
- public String getGrade() {
- return grade;
- }
- public void setGrade(String grade) {
- this.grade = grade;
- }
- public Identity getIdentity() {
- return identity;
- }
- public void setIdentity(Identity identity) {
- this.identity = identity;
- }
- public Student() {
- }
- public Student(String name, String stuNO, String grade) {
- this.name = name;
- this.stuNO = stuNO;
- this.grade = grade;
- }
- public Student deepClone()throws Exception{
- //将对象读如内存--序列化
- ByteArrayOutputStream byteArrayOutputStream=new ByteArrayOutputStream();
- ObjectOutputStream objectOutputStream=new ObjectOutputStream(byteArrayOutputStream);
- objectOutputStream.writeObject(this);
- //序列化的对象写入具体对象--反序列化
- ByteArrayInputStream byteArrayInputStream=new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
- ObjectInputStream objectInputStream=new ObjectInputStream(byteArrayInputStream);
- Student student=(Student)objectInputStream.readObject();
- return student;
- }
- //重写toString方法是为了演示
- @Override
- public String toString() {
- return "Student{" +
- "name='" + name + '\'' +
- ", stuNO='" + stuNO + '\'' +
- ", grade='" + grade + '\'' +
- ", identity=" + identity.toString() +
- '}';
- }
- public static void main(String[] fd){
- Student student=new Student("游坦之","11094","金庸群侠");
- Identity identity=new Identity("365214198710251123","聚贤山庄","102");
- student.setIdentity(identity);
- Student newStudent= null;
- try {
- newStudent = student.deepClone();
- System.out.println(newStudent);
- System.out.println("对比原型和克隆对象的引用数据类型是否是同一个:"+(newStudent.getIdentity()==student.getIdentity()));
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
打印结果:
Student{name='游坦之', stuNO='11094', grade='金庸群侠', identity=Identity{identityNo='365214198710251123', address='聚贤山庄', houseNumber='102'}}
对比原型和克隆对象的引用数据类型是否是同一个:false
三:应用
(1) 创建新对象成本较大(如初始化需要占用较长的时间,占用太多的CPU资源或网络资源),新的对象可以通过原型模式对已有对象进行复制来获得,如果是相似对象,则可以对其成员变量稍作修改。
(2) 如果系统要保存对象的状态,而对象的状态变化很小,或者对象本身占用内存较少时,可以使用原型模式配合备忘录模式来实
(3) 需要避免使用分层次的工厂类来创建分层次的对象,并且类的实例对象只有一个或很少的几个组合状态,通过复制原型对象得到新实例可能比使用构造函数创建一个新实例更加方便。