原型(Prototype
)模式的定义如下:用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。在这里,原型实例指定了要创建的对象的种类。用这种方式创建对象非常高效,根本无须知道对象创建的细节。例如,Windows 操作系统的安装通常较耗时,如果复制就快了很多。在生活中复制的例子非常多,这里不一一列举了。
原型模式的优点:
原型模式的克隆分为浅克隆和深克隆。
下面来看一个具体的例子:
创建一个 Horse 类对象,每调用一次 run 方法。该对象的 distance 属性都会递增 10 。
使用 Java 的 Object 的 浅克隆
方式:
Person
类必须先实现 Cloneable
接口。
package com.lsu.prototype.homework;
/**
* 抽象原型
*
* @author wang suo
* @version 1.0
* @date 2020/11/30 0030 13:22
*/
public interface Prototype {
/**
* 克隆自己
*
* @return 返回 obj
*/
public Object cloneSelf();
}
package com.lsu.prototype.homework;
/**
* 人
*
* @Author wang suo
* @Date 2020/11/30 0030 14:22
* @Version 1.0
*/
public class Person implements Cloneable, Prototype {
/**
* 坐骑
*/
private Horse mount;
Person(Horse horse) {
super();
this.mount = horse;
}
@Override
public Object cloneSelf() {
Object obj = null;
try {
obj = clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return obj;
}
/**
* 人跑路
*/
void run() {
// 使用坐骑跑路
mount.run();
// 显示路程
mount.show();
}
}
package com.lsu.prototype.homework;
import java.io.*;
/**
* 马
*
* @Author wang suo
* @Date 2020/11/30 0030 13:13
* @Version 1.0
*/
class Horse implements Serializable {
private int distance = 0;
/**
* 每次距离+10
*/
void run() {
this.distance += 10;
}
/**
* 显示路程
*/
void show() {
System.out.println("distance = " + this.distance);
}
}
package com.lsu.prototype.homework;
/**
* 测试类
*
* @Author wang suo
* @Date 2020/11/30 0030 13:12
* @Version 1.0
*/
public class Test {
public static void main(String[] args) {
Horse horse = new Horse();
// 第一个骑士
Person p1 = new Person(horse);
// 第二个骑士
Person p2 = (Person) p1.cloneSelf();
p1.run();
p2.run();
}
}
如上代码,我们克隆了一匹马 h2
我们想要的结果是这两头马各走了 10 米,但是结果却将这两者相加了,明明是两头马跑的距离却被算成了一头马跑的距离。
distance = 10
distance = 20
Process finished with exit code 0
这就是浅克隆,如果调用 clone 方法的当前对象拥有的成员变量是一个对象,那么 clone 方法仅仅是复制了当前对象所拥有的对象的 引用
,并没有复制这个对象所拥有的变量,所以还是同一个对象。
修改 Person
继承自 Serializable
接口。
package com.lsu.prototype.homework;
/**
* 抽象原型
*
* @author wang suo
* @version 1.0
* @date 2020/11/30 0030 13:22
*/
public interface Prototype {
/**
* 克隆自己
*
* @return 返回 obj
*/
Object cloneSelf();
}
package com.lsu.prototype.homework;
import java.io.*;
/**
* 人
*
* @Author wang suo
* @Date 2020/11/30 0030 14:22
* @Version 1.0
*/
public class Person implements Serializable, Prototype {
/**
* 坐骑
*/
private Horse mount;
Person(Horse horse) {
super();
this.mount = horse;
}
@Override
public Object cloneSelf() {
Object obj = null;
try {
//obj = clone();
// 使用深克隆
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
objectOutputStream.writeObject(this);
ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
obj = objectInputStream.readObject();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
return obj;
}
/**
* 人跑路
*/
void run() {
// 使用坐骑跑路
mount.run();
// 显示路程
mount.show();
}
}
package com.lsu.prototype.homework;
import java.io.*;
/**
* 马
*
* @Author wang suo
* @Date 2020/11/30 0030 13:13
* @Version 1.0
*/
class Horse implements Serializable {
private int distance = 0;
/**
* 每次距离+10
*/
void run() {
this.distance += 10;
}
/**
* 显示路程
*/
void show() {
System.out.println("distance = " + this.distance);
}
}
package com.lsu.prototype.homework;
/**
* 测试类
*
* @Author wang suo
* @Date 2020/11/30 0030 13:12
* @Version 1.0
*/
public class Test {
public static void main(String[] args) {
Horse horse = new Horse();
// 第一个骑士
Person p1 = new Person(horse);
// 第二个骑士
Person p2 = (Person) p1.cloneSelf();
p1.run();
p2.run();
}
}
测试可以实现深度复制,即包含的对象也不是同一个了。
distance = 10
distance = 10
Process finished with exit code 0
如果我们使用序列化技术将对象保存到本地,然后在想用的时候,再拿出来使用应该如何实现呢?
就像玩游戏的存档一样,玩了几关之后存档,之后再回来继续玩。
修改 Person
的代码如下即可:
package com.lsu.prototype.homework;
import java.io.*;
/**
* 人
*
* @Author wang suo
* @Date 2020/11/30 0030 14:22
* @Version 1.0
*/
public class Person implements Serializable, Prototype {
/**
* 坐骑
*/
private Horse mount;
Person(Horse horse) {
super();
this.mount = horse;
}
public Horse getMount() {
return mount;
}
@Override
public Object cloneSelf() {
Object obj = null;
try {
//obj = clone();
// 使用深克隆
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
objectOutputStream.writeObject(this);
ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
obj = objectInputStream.readObject();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
return obj;
}
/**
* 文件存档
*/
void archive(String path, String fileName) {
File file = new File(path, fileName);
boolean b = true;
ObjectOutputStream objectOutputStream = null;
try {
if (!file.exists()) {
b = file.createNewFile();
}
} catch (IOException e) {
e.printStackTrace();
}
if (b && file.exists()) {
// 写入到文件中
try {
objectOutputStream = new ObjectOutputStream(new FileOutputStream(file));
objectOutputStream.writeObject(this);
System.out.println("文件序列化成功,本地地址:"+ path + "文件名:" + fileName);
} catch (IOException e) {
e.printStackTrace();
} finally {
// 关闭输出流
try {
if (objectOutputStream != null) {
objectOutputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 读取存档
*/
Person readArchive(String path, String fileName) {
File file = new File(path, fileName);
ObjectInputStream objectInputStream = null;
Person obj = null;
// 写入
try {
objectInputStream = new ObjectInputStream(new FileInputStream(file));
obj = (Person) objectInputStream.readObject();
System.out.println("文件读取成功");
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
} finally {
// 关闭输入流
try {
if (objectInputStream != null) {
objectInputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return obj;
}
/**
* 人跑路
*/
void run() {
// 使用坐骑跑路
mount.run();
// 显示路程
mount.show();
}
}
public class Test {
public static void main(String[] args) {
Horse horse = new Horse();
// 第一个骑士
Person p1 = new Person(horse);
// 第二个骑士
Person p2 = (Person) p1.cloneSelf();
p1.run();
p2.run();
// fileName = "archive.txt"; path = "D:"
/*
保存到本地
*/
p2.archive("D:", "archive.txt");
System.out.println("---------------------------");
System.out.println("---p2 存档成功! 现在距离--" + p2.getMount().getDistance() + "米");
System.out.println("---------------------------");
/*
实现现场恢复
*/
Person p3 = p2.readArchive("D:", "archive.txt");
System.out.println("---------------------------");
System.out.println("---读取存档成功! 现在距离--" + p3.getMount().getDistance() + "米");
System.out.println("---------------------------");
p3.run();
}
}
这就是原型模式最佳实践。