原型模式

原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。

首先,既然原型模式需要创建当前对象的克隆,那么我们就不得不学习克隆(或者叫 拷贝)的知识了。

拷贝分为 浅拷贝 和 深拷贝

定义一个Car类(浅拷贝)

 1 package top.bigking.prototype;
 2 
 3 import java.util.Date;
 4 
 5 /**
 6  * @Author ABKing
 7  * @Date 2020/2/11 下午2:53
 8  * 浅拷贝
 9  **/
10 public class Car implements Cloneable {
11     private String name;
12     private Date date;
13 
14     @Override
15     protected Object clone() throws CloneNotSupportedException {
16         return super.clone();
17     }
18 
19     public Car() {
20     }
21 
22     public Car(String name, Date date) {
23         this.name = name;
24         this.date = date;
25     }
26 
27     public String getName() {
28         return name;
29     }
30 
31     public void setName(String name) {
32         this.name = name;
33     }
34 
35     public Date getDate() {
36         return date;
37     }
38 
39     public void setDate(Date date) {
40         this.date = date;
41     }
42 }

定义一个Car2类(深拷贝)

 1 package top.bigking.prototype;
 2 
 3 import java.util.Date;
 4 
 5 /**
 6  * @Author ABKing
 7  * @Date 2020/2/11 下午5:39
 8  * 深拷贝
 9  **/
10 public class Car2 implements Cloneable {
11     private String name;
12     private Date date;
13 
14     @Override
15     protected Object clone() throws CloneNotSupportedException {
16         Object obj = super.clone();
17         Car2 car = (Car2) obj;
18         car.setDate((Date) this.date.clone());
19         return obj;
20     }
21 
22     public Car2() {
23     }
24 
25     public Car2(String name, Date date) {
26         this.name = name;
27         this.date = date;
28     }
29 
30     public String getName() {
31         return name;
32     }
33 
34     public void setName(String name) {
35         this.name = name;
36     }
37 
38     public Date getDate() {
39         return date;
40     }
41 
42     public void setDate(Date date) {
43         this.date = date;
44     }
45 }

测试:

 1 package top.bigking.prototype;
 2 
 3 import org.junit.Test;
 4 
 5 import java.util.Date;
 6 
 7 /**
 8  * @Author ABKing
 9  * @Date 2020/2/11 下午4:40
10  **/
11 public class TestPrototype {
12     //浅拷贝
13     @Test
14     public void testShallowCopy() throws CloneNotSupportedException {
15         Date date = new Date(11314211L);
16         Car car1 = new Car("兰博基尼", date);
17         Car car2 = (Car) car1.clone(); //拷贝
18 
19         System.out.println("第一辆车:" + car1 + car1.getName() + "-----" + car1.getDate());
20         System.out.println("第二辆车:" + car2 + car2.getName() + "-----" + car2.getDate());
21 
22         date.setTime(4444524L);
23 
24         System.out.println("--------修改date后-------");
25 
26         System.out.println("第一辆车:" + car1 + car1.getName() + "-----" + car1.getDate());
27         System.out.println("第二辆车:" + car2 + car2.getName() + "-----" + car2.getDate());
28     }
29     //深拷贝
30     @Test
31     public void testDeepCopy() throws CloneNotSupportedException {
32         Date date = new Date(11314211L);
33         Car2 car1 = new Car2("兰博基尼", date);
34         Car2 car2 = (Car2) car1.clone(); //拷贝
35 
36         System.out.println("第一辆车:" + car1 + car1.getName() + "-----" + car1.getDate());
37         System.out.println("第二辆车:" + car2 + car2.getName() + "-----" + car2.getDate());
38 
39         date.setTime(4444524L);
40 
41         System.out.println("--------修改date后-------");
42 
43         System.out.println("第一辆车:" + car1 + car1.getName() + "-----" + car1.getDate());
44         System.out.println("第二辆车:" + car2 + car2.getName() + "-----" + car2.getDate());
45     }
46 }

运行结果分别为:

1 /usr/local/java/jdk1.8.0_231/bin/java -ea -Didea.test.cyclic.buffer.size=1048576 -javaagent:/usr/local/bin/idea-IU-193.5662.53/lib/idea_rt.jar=37975:/usr/local/bin/idea-IU-193.5662.53/bin -Dfile.encoding=UTF-8 -classpath /usr/local/bin/idea-IU-193.5662.53/lib/idea_rt.jar:/usr/local/bin/idea-IU-193.5662.53/plugins/junit/lib/junit5-rt.jar:/usr/local/bin/idea-IU-193.5662.53/plugins/junit/lib/junit-rt.jar:/usr/local/java/jdk1.8.0_231/jre/lib/charsets.jar:/usr/local/java/jdk1.8.0_231/jre/lib/deploy.jar:/usr/local/java/jdk1.8.0_231/jre/lib/ext/cldrdata.jar:/usr/local/java/jdk1.8.0_231/jre/lib/ext/dnsns.jar:/usr/local/java/jdk1.8.0_231/jre/lib/ext/jaccess.jar:/usr/local/java/jdk1.8.0_231/jre/lib/ext/jfxrt.jar:/usr/local/java/jdk1.8.0_231/jre/lib/ext/localedata.jar:/usr/local/java/jdk1.8.0_231/jre/lib/ext/nashorn.jar:/usr/local/java/jdk1.8.0_231/jre/lib/ext/sunec.jar:/usr/local/java/jdk1.8.0_231/jre/lib/ext/sunjce_provider.jar:/usr/local/java/jdk1.8.0_231/jre/lib/ext/sunpkcs11.jar:/usr/local/java/jdk1.8.0_231/jre/lib/ext/zipfs.jar:/usr/local/java/jdk1.8.0_231/jre/lib/javaws.jar:/usr/local/java/jdk1.8.0_231/jre/lib/jce.jar:/usr/local/java/jdk1.8.0_231/jre/lib/jfr.jar:/usr/local/java/jdk1.8.0_231/jre/lib/jfxswt.jar:/usr/local/java/jdk1.8.0_231/jre/lib/jsse.jar:/usr/local/java/jdk1.8.0_231/jre/lib/management-agent.jar:/usr/local/java/jdk1.8.0_231/jre/lib/plugin.jar:/usr/local/java/jdk1.8.0_231/jre/lib/resources.jar:/usr/local/java/jdk1.8.0_231/jre/lib/rt.jar:/home/king/IdeaProjects/GOF_23/target/test-classes:/home/king/IdeaProjects/GOF_23/target/classes:/home/king/maven/repository/junit/junit/4.10/junit-4.10.jar:/home/king/maven/repository/org/hamcrest/hamcrest-core/1.1/hamcrest-core-1.1.jar com.intellij.rt.junit.JUnitStarter -ideVersion5 -junit4 top.bigking.prototype.TestPrototype,testShallowCopy
2 第一辆车:top.bigking.prototype.Car@579bb367兰博基尼-----Thu Jan 01 11:08:34 CST 1970
3 第二辆车:top.bigking.prototype.Car@255316f2兰博基尼-----Thu Jan 01 11:08:34 CST 1970
4 --------修改date后-------
5 第一辆车:top.bigking.prototype.Car@579bb367兰博基尼-----Thu Jan 01 09:14:04 CST 1970
6 第二辆车:top.bigking.prototype.Car@255316f2兰博基尼-----Thu Jan 01 09:14:04 CST 1970
7 
8 Process finished with exit code 0

可以很明显的看到,修改date后,时间并没有发生改变

运行第二个JUnit测试:

1 /usr/local/java/jdk1.8.0_231/bin/java -ea -Didea.test.cyclic.buffer.size=1048576 -javaagent:/usr/local/bin/idea-IU-193.5662.53/lib/idea_rt.jar=36785:/usr/local/bin/idea-IU-193.5662.53/bin -Dfile.encoding=UTF-8 -classpath /usr/local/bin/idea-IU-193.5662.53/lib/idea_rt.jar:/usr/local/bin/idea-IU-193.5662.53/plugins/junit/lib/junit5-rt.jar:/usr/local/bin/idea-IU-193.5662.53/plugins/junit/lib/junit-rt.jar:/usr/local/java/jdk1.8.0_231/jre/lib/charsets.jar:/usr/local/java/jdk1.8.0_231/jre/lib/deploy.jar:/usr/local/java/jdk1.8.0_231/jre/lib/ext/cldrdata.jar:/usr/local/java/jdk1.8.0_231/jre/lib/ext/dnsns.jar:/usr/local/java/jdk1.8.0_231/jre/lib/ext/jaccess.jar:/usr/local/java/jdk1.8.0_231/jre/lib/ext/jfxrt.jar:/usr/local/java/jdk1.8.0_231/jre/lib/ext/localedata.jar:/usr/local/java/jdk1.8.0_231/jre/lib/ext/nashorn.jar:/usr/local/java/jdk1.8.0_231/jre/lib/ext/sunec.jar:/usr/local/java/jdk1.8.0_231/jre/lib/ext/sunjce_provider.jar:/usr/local/java/jdk1.8.0_231/jre/lib/ext/sunpkcs11.jar:/usr/local/java/jdk1.8.0_231/jre/lib/ext/zipfs.jar:/usr/local/java/jdk1.8.0_231/jre/lib/javaws.jar:/usr/local/java/jdk1.8.0_231/jre/lib/jce.jar:/usr/local/java/jdk1.8.0_231/jre/lib/jfr.jar:/usr/local/java/jdk1.8.0_231/jre/lib/jfxswt.jar:/usr/local/java/jdk1.8.0_231/jre/lib/jsse.jar:/usr/local/java/jdk1.8.0_231/jre/lib/management-agent.jar:/usr/local/java/jdk1.8.0_231/jre/lib/plugin.jar:/usr/local/java/jdk1.8.0_231/jre/lib/resources.jar:/usr/local/java/jdk1.8.0_231/jre/lib/rt.jar:/home/king/IdeaProjects/GOF_23/target/test-classes:/home/king/IdeaProjects/GOF_23/target/classes:/home/king/maven/repository/junit/junit/4.10/junit-4.10.jar:/home/king/maven/repository/org/hamcrest/hamcrest-core/1.1/hamcrest-core-1.1.jar com.intellij.rt.junit.JUnitStarter -ideVersion5 -junit4 top.bigking.prototype.TestPrototype,testDeepCopy
2 第一辆车:top.bigking.prototype.Car2@579bb367兰博基尼-----Thu Jan 01 11:08:34 CST 1970
3 第二辆车:top.bigking.prototype.Car2@255316f2兰博基尼-----Thu Jan 01 11:08:34 CST 1970
4 --------修改date后-------
5 第一辆车:top.bigking.prototype.Car2@579bb367兰博基尼-----Thu Jan 01 09:14:04 CST 1970
6 第二辆车:top.bigking.prototype.Car2@255316f2兰博基尼-----Thu Jan 01 11:08:34 CST 1970
7 
8 Process finished with exit code 0

可以看到,时间已经发生了变化。

 

什么时候会用到拷贝呢?

答案是 当使用new关键字创建对象太耗时时,使用拷贝的方法可以大大加快速度

 1 package top.bigking.prototype;
 2 
 3 import org.junit.Test;
 4 
 5 import java.io.ObjectInputStream;
 6 
 7 /**
 8  * @Author ABKing
 9  * @Date 2020/2/13 下午5:39
10  **/
11 public class TestPrototype {
12     //测试直接new的方式
13     @Test
14     public void testNew(){
15         long start = System.currentTimeMillis();
16         for (int i = 0; i < 1000; i++) {
17             TestCar car = new TestCar();
18         }
19         long end = System.currentTimeMillis();
20         System.out.println("使用new的方法总耗时:" + (end - start));
21 
22     }
23     @Test
24     public void testCopy() throws CloneNotSupportedException {
25         long start = System.currentTimeMillis();
26         TestCar car1 = new TestCar();
27         for (int i = 0; i < 1000; i++) {
28             TestCar car2 = (TestCar) car1.clone();
29         }
30         TestCar car2 = (TestCar) car1.clone();
31         long end = System.currentTimeMillis();
32         System.out.println("使用clone()的方法总耗时: " + (end - start));
33     }
34 }
35 class TestCar implements Cloneable{
36     public TestCar(){
37         try {
38             Thread.sleep(10); // 模拟创建对象的时间
39         } catch (InterruptedException e) {
40             e.printStackTrace();
41         }
42     }
43 
44     @Override
45     protected Object clone() throws CloneNotSupportedException {
46         return super.clone();
47     }
48 }

 

运行第一个JUnit测试,执行结果如下:

使用new的方法总耗时:10121

运行第二个JUnit测试,执行结果如下:

使用clone()的方法总耗时: 11

可以看到,差距非常明显,使用clone()方法,几乎不耗时间

 当然,值得注意的是,当new非常耗时的时候,两者差距才很明显,如果把本例中的Thread.sleep(10)注释掉,两者所耗时均为0,几乎不耗时间。

 

开发中的应用场景:

原型模式一般很少单独出现,一般是和工厂方法模式一起出现,通过clone的方法创建一个对象,然后由工厂方法提供给调用者。

spring中bean对象的创建实际就是两种:单例模式和原型模式(当然,原型模式需要和工厂模式搭配起来)。

posted @ 2020-02-11 23:23  ABKing  阅读(140)  评论(0编辑  收藏  举报