前言#
今天是周五了,在美好的周末到来之前赶紧写点东西来证明自己这周没有当一条咸鱼。
原型模式:用原型实例指定创建对象的种类,并且通过拷贝这些原型来创建新的对象。
这句话看着高大上,实际上是我从网上拷贝过来的,原型模式跟我们之前的写过的几种模式,例如观察者,代理模式等等,是不一样的,与其说他是一种模式,不如说是一种优化,因为他并没有改变原来的代码逻辑结构,仅仅是在创建对象上,进行了优化。
正文#
Object本身就有clone方法,这个方法是protect修饰符,所以我们需要把clone方法暴露出来,看一下Student的代码示例:
public class Student implements Cloneable {
private String name = "xxx";
private String sex = "man";
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException e) {
return new Student();
}
}
@Override
public String toString() {
return "my name is " + getName() + ":" + hashCode();
}
}
要想把clone方法暴露出来,需要一下两个步骤:
1、实现Cloneable接口,虽然这个接口里并没有方法。
2、创建一个public方法,调用clone方法,并实现自己的逻辑。
ok,运行一下,看看有没有效果:
Student student = new Student();
// 修改一下默认的student名字,为了证明调用的是clone方法,而不是new
student.setName("lisi");
Student student1 = (Student) student.clone();
System.out.println(student.toString());
System.out.println(student1.toString());
从上面的运行结果上看,通过clone方法创建并返回了一个新的Student对象。
那么new创建对象和clone创建对象的运行效率如何呢?我们分别创建10000个,对比一下耗时的时间:
从结果上看,创建10000个对象,clone的耗时要比new多耗费了5毫秒,虽然速度稍慢,但是效率还是不错的。
(别着急,关于这个效率的问题,后面还有更具体的验证)
既然new关键字的效率比clone要高,我们还要clone有个卵用??
原型模式的使用场景
为什么要使用原型模式呢?首先我们要回到我们学习设计模式的起点:
设计模式更多的是为了提高代码可维护性,扩展性,可读性,对各个模块之间进行解耦,利于开发和维护。
有些时候我们不得不牺牲掉在可以接受的范围内的运行效率,来完成目标模式。
鱼和熊掌不可得兼,所以我们要权衡牺牲的代价和得到的收益,过分追求效率,可能会让我们迷失了真正的目标。
说了这么多,原型模式适合什么样的场景呢:
某一个类(Class),可以大量复用已有的对象的属性,适合原型模式。
也就是说,我们需要的类的新对象和已有的对象的相比,几乎不需要修改就可以直接用,而且属性越多,原型模式体现的优势越好,光说不练没有用,下面就举个例子:
某市进行高效运动会,我们需要统计各个学校参加的学生,学生资料中,包含了学校的信息。
先定义学校School对象:
/**
* Created by li.zhipeng on 2017/9/15.
* <p>
* 自定义学生类
*/
public class Student implements Cloneable {
// 以下全是学校资料
private String area; // 所在的区
private String address; // 地址
private String slogan; // 学校的宣传语
private int level; // 学校的级别
private int teacherNum; // 老师的数量
private int studentNum; // 学生的数量
... // 还有很多信息
// 以下全是学生资料
private String name = "xxx";
private String sex = "boy";
}
我们看到在Student中,如果是同一个学校的学生,我们只要修改其中的name和sex就可以了,我有两种方法通过学生a,得到同一个学校的同学b的信息:
// 通过new
Student b = new Student();
b.setArea(a.getArea());
b.setAddress(a.getAddress());
b.setSlogan(a.getSlogan());
b.setLevel(a.getLevel());
b.setTeacherNum(a.getTeacherNum());
b.setStudentNum(a.getStudentNum());
b.setName("zhangsan");
b.setSex("boy");
// 通过clone
Student c = (Student) a.clone();
c.setName("wangwu");
c.setSex("boy");
是不是对比非常明显,相信这个时候你已经看到了原型模式的代码是多么的简洁。
为clone的效率讨一个说法
我们刚才验证了,如果不重写构造方法,属性很少的情况下,new确实比clone的效率要高,但是如果需要重写构造方法,或者是属性很多呢?
1、通过new关键字,创建属性很多的对象,取3组数据:
long time = System.currentTimeMillis();
for (int i = 0; i < length; i++) {
Student student = new Student();
}
// 打印时间...
time = System.currentTimeMillis();
for (int i = 0; i < length; i++) {
Student student1 = (Student) original.clone();
}
// 打印时间...
经过对比,如果是创建对象属性很多的对象,new比clone确实要快一些。
2、对比new创建了之后,还要通过setter方法改变属性,去3组数据:
long time = System.currentTimeMillis();
for (int i = 0; i < length; i++) {
Student b = new Student();
b.setArea(original.getArea());
b.setAddress(original.getAddress());
b.setSlogan(original.getSlogan());
b.setLevel(original.getLevel());
b.setTeacherNum(original.getTeacherNum());
b.setStudentNum(original.getStudentNum());
b.setName("zhangsan");
b.setSex("boy");
}
// 打印时间...
time = System.currentTimeMillis();
for (int i = 0; i < length; i++) {
Student c = (Student) original.clone();
c.setName("wangwu");
c.setSex("boy");
}
// 打印时间...
由结果我们可以看出,setter方法是比较耗时的,通过原型模式,我们尽可能的避免setter,所以在运行时间上已经显示出优势。
3、重写Student默认的构造方法,取3组数据:
public Student(){
System.out.println("Create Student Object...");
}
// 测试代码与第2中情况相同
是不是觉得十分意外,从结果上我们发现了clone是会调用类的构造方法的,仅仅是对对象的克隆,所以在运行时间上的优势大幅提升。
总结#
回顾一下今天的内容:
1、首先我们了解了原型模式的定义,以及clone方法的使用步骤。
2、了解了原型模式的使用场景,实际体会原型模式在代码上为我们带来的简洁体验。
3、根据不同情况,对比new和clone创建对象的效率,clone的运行效率在大部分应用场景会让创建对象的速度更快,效率更高。
ok,今天所有的内容就结束了,祝大家周末愉快~