原型模式解决的主要问题是如何快速的复制一个已经存在的对象,一个普遍的做法是构建一个属于相同类的对象,然后遍历原始对象的所有属性值并复制到新对象中。这样的做法有一些问题,不是每一个对象都可以通过这种方式进行复制,且这么做的编程代价过高,比方说:
class Main{
public static void main(String[] args) {
Sheep sheep = new Sheep("tom", 1, "red");
new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
}
}
这样做比较好理解,简单易于操作,但是复制对象的效率很低(现在只有三个参数需要处理)。而原型模式就可以解决这个问题,原型模式是用于创建重复的对象,同时又能保证性能的一种模式,用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
原型模式的角色如下:
clone()
方法,一般也只有这么一个方法。clone()
方法,它是可被复制的对象。clone()
方法来复制新的对象。原型模式的优点在于:
原型模式的缺点在于:
原型模式的克隆分为浅克隆和深克隆:
浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。
深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。
Java 中的 Object类提供了 clone()
方法,该方法实现了浅克隆,在 Java 中 Cloneable 接口就是原型模式中的抽象原型类,而任何实现了 Cloneable 接口的类就是具体原型类,代码如下:
public class Realizetype implements Cloneable {
public Realizetype() {
System.out.println("具体原型创建完成");
}
@Override
protected Realizetype clone() throws CloneNotSupportedException {
System.out.println("具体原型复制成功");
return (Realizetype) super.clone();
}
}
测试类:
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
Realizetype prototye = new Realizetype();
Realizetype clonedObject = prototye.clone();
// false
System.out.println(prototye == clonedObject);
}
}
上面的拷贝方式是浅拷贝,如果需要实现深拷贝,可以考虑使用序列化、反序列化的方式进行实现,示例代码如下:
public class Sheep implements Cloneable, Serializable {
...
// 深克隆方法
public Sheep deepClone() throws IOException {
//创建对象流对象
ObjectInputStream ois = new ObjectInputStream(Files.newInputStream(Paths.get("a.txt")));
ObjectOutputStream oos = new ObjectOutputStream(Files.newOutputStream(Paths.get("a.txt")));
try {
//序列化
oos.writeObject(this);
//反序列化
Sheep newSheep = (Sheep) ois.readObject();
return newSheep;
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
return null;
} finally {
oos.close();
ois.close();
}
}
}
小结一下,原型模式的实现思想如下:
个人认为原型模式的一个重要应用在于减少保护性拷贝的代码量,保护性拷贝是指为了防止客户端对类的约束条件产生破坏,传递对象的时候要进行拷贝,一个简单的示例如下:
public class Period {
private final Date start;
private final Date end;
public Period(Date start, Date end) {
this.start = new Date(start.getTime());
this.end = new Date(end.getTime());
if (this.start.compareTo(end) > 0){
throw new IllegalArgumentException(this.start + "after" + this.end);
}
}
public Date getStart() {
// 直接return会破坏类
return new Date(start.getTime());
}
public Date getEnd() {
// 直接return会破坏类
return new Date(end.getTime());
}
}
类的约束条件是开始时间要小于结束时间,且这两个成员变量是私有的,但是如果 getter
方法中直接返回原始对象,那么就会破坏原本的约束,也就是说如果一个类从客户端得到或者返回一个可变组件,那么就必须进行保护性拷贝。如果使用了原型模式,直接返回要进行保护对象的 clone()
方法的返回值即可,这样大大减少了代码的书写量。
除此之外还可以创建一个中心化原型注册表,用于存储常用原型。可以新建一个工厂类来实现注册表,或者添加一个静态方法,不管是哪一种方式,这些方法必须能够根据客户端代码设定的条件进行搜索。搜索条件可以是简单的字符串,或者是一组复杂的搜索参数。找到合适的原型后,注册表应对原型进行克隆,并将复制生成的对象返回给客户端。
文中难免会出现一些描述不当之处(尽管我已反复检查多次),欢迎在留言区指正,相关的知识点也可进行分享,希望大家都能有所收获!!如果觉得我的文章写得还行,不妨支持一下。你的每一个转发、关注、点赞、评论都是对我最大的支持!