如果这篇博客帮助到你,可以请我喝一杯咖啡~
CC BY 4.0 (除特别声明或转载文章外)
原型(Prototype)模式的定义如下:用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。在这里,原型实例指定了要创建的对象的种类。
1、实现克隆操作,在 JAVA 继承 Cloneable,重写 clone()
或通过序列化
的方式来实现深拷贝。
2、原型模式同样用于隔离类对象的使用者和具体类型(易变类)之间的耦合关系,它同样要求这些”易变类”拥有稳定的接口。
原型模式的结构
原型模式包含以下主要角色:
- 抽象原型类:规定了具体原型对象必须实现的接口。
- 具体原型类:实现抽象原型类的 clone() 方法,它是可被复制的对象。
- 访问类:使用具体原型类中的 clone() 方法来复制新的对象。
interface ProtoType{
ProtoType clone();
}
class ConcreteProtoType implements ProtoType{
@Override
public ProtoType clone() {
//克隆操作
}
}
class Client{
public static void main(String[] args) {
ConcreteProtoType obj = new ConcreteProtoType();
ConcreteProtoType objVlone = (ConcreteProtoType) obj.clone();
}
}
原型模式实现
原型模式的克隆分为浅拷贝和深拷贝。
浅拷贝:创建一个新对象,新对象的属性和原来对象完全相同,对于引用类型属性,仍指向原有属性所指向的对象的内存地址。
深拷贝:创建一个新对象,属性中引用类型也会被克隆,不再指向原有对象地址。
浅拷贝
Java 中的 Object 类提供了浅克隆的 clone() 方法,具体原型类只要实现 Cloneable 接口就可实现对象的浅克隆,这里的 Cloneable 接口就是抽象原型类。
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
ConcreteProtoType obj = new ConcreteProtoType("原型", new ConcreteProtoType("引用", null));
ConcreteProtoType clone = (ConcreteProtoType) obj.clone();
System.out.println("obj == clone => " + (obj == clone));
System.out.println("-------------------");
System.out.println("obj.getQuote() == clone.getQuote() => " + (obj.getQuote() == clone.getQuote()));
}
}
class ConcreteProtoType implements Cloneable{
private String name;
private ConcreteProtoType quote;
public ConcreteProtoType(String name, ConcreteProtoType quote) {
this.name = name;
this.quote = quote;
}
public ConcreteProtoType getQuote() {
return quote;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
输出结果:
obj == clone => false
-------------------
obj.getQuote() == clone.getQuote() => true
可见浅拷贝对象中的引用类型只是拷贝了引用对象的地址,并没有拷贝引用对象。
深拷贝
方式一(使用clone()方法)
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
ConcreteProtoType obj = new ConcreteProtoType("原型", new ConcreteProtoType("引用", null));
ConcreteProtoType clone = (ConcreteProtoType) obj.clone();
System.out.println("obj == clone:" + (obj == clone));
System.out.println("-------------------");
System.out.println("obj.getQuote() == clone.getQuote():" + (obj.getQuote() == clone.getQuote()));
}
}
class ConcreteProtoType implements Cloneable{
private String name;
private ConcreteProtoType quote;
public ConcreteProtoType(String name, ConcreteProtoType quote) {
this.name = name;
this.quote = quote;
}
public ConcreteProtoType getQuote() {
return quote;
}
public void setQuote(ConcreteProtoType quote) {
this.quote = quote;
}
@Override
protected Object clone() throws CloneNotSupportedException {
ConcreteProtoType clone = (ConcreteProtoType) super.clone();
if (clone.getQuote() != null){
clone.setQuote((ConcreteProtoType) clone.getQuote().clone());
}
return clone;
}
}
输出结果:
obj == clone => false
-------------------
obj.getQuote() == clone.getQuote() => false
方式二(使用序列化)
public class Client {
public static void main(String[] args){
ConcreteProtoType obj = new ConcreteProtoType("原型", new ConcreteProtoType("引用", null));
ConcreteProtoType clone = (ConcreteProtoType) obj.deepClone();
System.out.println("obj == clone => " + (obj == clone));
System.out.println("-------------------");
System.out.println("obj.getQuote() == clone.getQuote() => " + (obj.getQuote() == clone.getQuote()));
}
}
class ConcreteProtoType implements Serializable{
private String name;
private ConcreteProtoType quote;
public ConcreteProtoType(String name, ConcreteProtoType quote) {
this.name = name;
this.quote = quote;
}
public ConcreteProtoType getQuote() {
return quote;
}
public void setQuote(ConcreteProtoType quote) {
this.quote = quote;
}
public Object deepClone(){
//创建流对象
ByteArrayOutputStream bos = null;
ObjectOutputStream oos = null;
ByteArrayInputStream bis = null;
ObjectInputStream ois = null;
try {
//序列化
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos);
//当前这个对象以对象流的方式输出
oos.writeObject(this);
//反序列化
bis = new ByteArrayInputStream(bos.toByteArray());
ois = new ObjectInputStream(bis);
return ois.readObject();
}catch (Exception e){
e.printStackTrace();
return null;
}finally {
try {
ois.close();
bis.close();
oos.close();
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
输出结果:
obj == clone => false
-------------------
obj.getQuote() == clone.getQuote() => false
原型模式优缺点
原型模式的优点:
- Java 自带的原型模式基于内存二进制流的复制,在性能上比直接 new 一个对象更加优良。
- 可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份,并将其状态保存起来,简化了创建对象的过程,以便在需要的时候使用(例如恢复到历史某一状态),可辅助实现撤销操作。
原型模式的缺点: - 需要为每一个类都配置一个 clone 方法
- clone 方法位于类的内部,当对已有类进行改造的时候,需要修改代码,违背了开闭原则。
- 当实现深克隆时,需要编写较为复杂的代码,而且当对象之间存在多重嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来会比较麻烦。因此,深克隆、浅克隆需要运用得当。
使用场景
1、资源优化场景。
2、类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。
3、性能和安全要求的场景。
4、通过 new 产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。
5、一个对象多个修改者的场景。
6、一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。
7、在实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过 clone 的方法创建一个对象,然后由工厂方法提供给调用者。原型模式已经与 Java 融为浑然一体,大家可以随手拿来使用。