模式定义与特点
- 定义
用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。比如Ctrl+C,V就能快速的完成文件的复制粘贴
也就是说我们通过原型模式可以通过复制(拷贝、克隆)原型对象快速的创建一个新的同类型的对象,而不需要提供专门的new()操作来完成对象的创建。
- 问题由来
当对象的构造函数非常复杂,在生成新对象的时候非常耗时间、耗资源的情况,我们可以使用原型模式来进行对象的创建
当需要提供数据对象,同时又需要避免外部对数据对象进行修改。
参与角色
- 抽象原型类:规定了具体原型对象必须实现的接口。
- 具体原型类:实现抽象原型类的 clone() 方法,它是可被复制的对象。
- 类结构图
原型模式主要用于对象的复制,它的核心是就是类图中的原型类Prototype
。Prototype
类需要具备以下两个条件:
- 实现
Cloneable
接口。Cloneable
接口的作用只有一个,就是在运行时通知虚拟机可以安全地在实现了此接口的类上使用clone方法。虚拟机中,只有实现了这个接口的类才可以被拷贝,否则在运行时会抛出CloneNotSupportedException
异常。 - 重写Object类中的clone方法。clone方法作用是返回对象的一个拷贝,但是该方法的访问修饰符是
protected
类型的,其他包下的类无法调用该方法,因此我们一般会将Prototype
类中的clone方法修改为public类型。
原型模式是一种比较简单的模式,也非常容易理解,实现一个接口,重写一个方法即完成了原型模式。在实际应用中,原型模式很少单独出现。经常与其他模式混用,他的原型类Prototype也常用抽象类来替代。
案例实现
抽象原型类
abstract class Prototype implements Cloneable { @Override public Prototype clone() throws CloneNotSupportedException { return (Prototype)super.clone(); } public abstract void show(); }
具体原型类
class ConcretePrototype extends Prototype { public void show() { System.out.println("原型模式实现类"); } }
- 客户端调用
public class Client { public static void main(String[] args){ Prototype cp = new ConcretePrototype(); for(int i=0; i< 10; i++){ Prototype clonecp = cp.clone(); clonecp.show(); } } }
原型模式的克隆分为浅克隆和深克隆,Java 中的 Object 类提供了浅克隆的 clone() 方法,具体原型类只要实现 Cloneable 接口就可实现对象的浅克隆。
深拷贝与浅拷贝
浅拷贝:被拷贝对象的所有变量都含有与原对象相同的值,而且对其他对象的引用仍然是指向原来的对象。即浅拷贝只负责当前对象实例,对引用的对象不做拷贝。
ArrayList<String> a = new ArrayList(); ArrayList<String> b = a; //当修改a时,b的值同样会被修改,浅拷贝不适合在原型模式中使用
- 深拷贝:
被拷贝对象的所有的变量都含有与原来对象相同的值,除了那些引用其他对象的变量。那些引用其他对象的变量将指向一个被拷贝的新对象,而不再是原有那些被引用对象。即 深拷贝把要拷贝的对象所引用的对象也都拷贝了一次,而这种对被引用到的对象拷贝叫做间接拷贝。
如果想要实现深拷贝,可以通过覆盖Object中的clone方法的方式,并且在clone方法内部,把该对象引用的其他对象也要clone一份 , 这就要求这个被引用的对象必须也要实现Cloneable接口并且实现clone方法。深拷贝存在一个问题,到底拷贝多深才算深拷贝?至于彻底深拷贝,几乎是不可能实现的。
总结
- 对象之间相同或相似,即只是个别的几个属性不同的时候。
- 对象的创建过程比较麻烦,但复制比较简单的时候。
比如需要在一个循环体内创建对象,假如对象创建过程比较复杂或者循环次数很多的话,使用原型模式不但可以简化创建过程,而且可以使系统的整体性能提高很多。
- 使用注意事项
使用原型模式复制对象不会调用类的构造方法。因为对象的复制是通过调用Object类的clone方法来完成的,它直接在内存中复制数据,因此不会调用到类的构造方法。不但构造方法中的代码不会执行,甚至连访问权限都对原型模式无效。还记得单例模式吗?单例模式中,只要将构造方法的访问权限设置为private型,就可以实现单例。但是clone方法直接无视构造方法的权限,所以,单例模式与原型模式是冲突的,在使用时要特别注意。