1、简介
原型模式使用原型实例指定创建对象的种类,并且通过拷贝原型对象创建新的对象。提供了应该通过已存在对象进行新对象创建的接口clone。原型模式实际上就是从一个对象再创建另外一个可定制的对象,并且不需要知道创建的细节。在初始化的信息不发生变化的情况下,克隆是最好的办法,既隐藏了对象创建的细节,又大大提高了性能。因为如果不用clone,每次new都需要执行一次构造函数,如果构造函数执行时间很长,那么多次的执行初始化操作太低效了。
原型模式实现clone接口的时候必须使用深拷贝。
原型模式的重点在从自身赋值自己创建新的类对象,隐藏创建的细节。
2、类图
3、原型模式角色
(1)抽象原型(Prototype)角色:规定了具体原型对象必须实现的接口(如果要提供深拷贝,则必须具有实现clone的规定);
(2)具体原型(Concrete Prototype):从抽象原型派生而来,是客户程序使用的对象,即被复制的对象,需要实现抽象原型角色所要求的接口;
(3)客户端(Client):使用原型对象的客户端程序。
4、原型模式使用场景
原型模式的主要思想是基于现有的对象克隆一个新的对象出来,一般是由对象的内部提供克隆的方法,通过clone方法返回一个对象的副本。
如:
- 当一个系统应该独立于它的产品创建、构成和表示时,要使用原型模式;
- 当要实例化的类是在运行时刻指定时,如通过动态装载;
- 为了避免创建一个与产品类层次平行的工厂类层次时;
- 当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆原型可能比每次用合适的状态手工实例化原型类更方便一些。
5、优缺点
优点:
- 原型模式对客户隐藏了具体的产品类;
- 运行时刻增加和删除产品: 原型模式允许只通过客户注册原型实例就可以将一个新的具体产品类并入系统;
- 改变值以指定新对象: 高度动态的系统允许通过对象复合定义新的行为。如通过为一个对象变量指定值并且不定义新的类。通过实例化已有类并且将实例注册为客户对象的原型,就可以有效定义新类别的对象。客户可以将职责代理给原型,从而表现出新的行为;
- 改变结构以指定新对象:许多应用由部件和子部件来创建对象;
- 减少子类的构造,Prototype模式克隆一个原型而不是请求一个工厂方法去产生一个新的对象;
- 用类动态配置应用 一些运行时刻环境允许动态将类装载到应用中;
- 使用原型模式创建对象比直接new一个对象在性能上要好的多,因为Object类的clone方法是一个本地方法,直接操作内存中的二进制流,特别是复制大对象时,性能的差别非常明显;
- 使用原型模式的另一个好处是简化对象的创建,使得创建对象很简单。
缺点:
原型模式的主要缺陷是每一个抽象原型Prototype的子类都必须实现clone操作,实现clone函数可能会很困难。当所考虑的类已经存在时就难以新增clone操作,当内部包括一些不支持拷贝或有循环引用的对象时,实现克隆可能也会很困难的。
6、浅拷贝与深拷贝
(1)浅拷贝
被拷贝对象的所有变量都含有与原对象相同的值,而且对其他对象的引用仍然是指向原来的对象。即浅拷贝只负责当前对象实例,对引用的对象不做拷贝。
(2)深拷贝
被拷贝对象的所有的变量都含有与原来对象相同的值,除了引用其他对象的变量。引用其他对象的变量将指向一个被拷贝的新对象,而不再是原有被引用对象。即深拷贝把要拷贝的对象所引用的对象也都拷贝了一次。
深拷贝要深入到多少层,是一个不确定的问题。在决定以深拷贝的方式拷贝一个对象的时候,必须决定对间接拷贝的对象是采取浅拷贝还是深拷贝还是继续采用深拷贝。因此,在采取深拷贝时,需要决定多深才算深。此外,在深拷贝的过程中,很可能会出现循环引用的问题。
7、代码实例
(1)浅拷贝
1 /** 2 * @author it-小林 3 * @desc 4 * @date 2021年07月14日 20:08 5 * 步骤 6 * 1、实现一个接口 Cloneable 7 * 2、重写一个方法 clone() 8 */ 9 public class Video implements Cloneable { 10 11 private String name; 12 13 private Date createTime; 14 15 16 @Override 17 protected Object clone() throws CloneNotSupportedException { 18 return super.clone(); 19 } 20 21 public Video() { 22 } 23 24 public Video(String name, Date createTime) { 25 this.name = name; 26 this.createTime = createTime; 27 } 28 29 public String getName() { 30 return name; 31 } 32 33 public void setName(String name) { 34 this.name = name; 35 } 36 37 public Date getCreateTime() { 38 return createTime; 39 } 40 41 public void setCreateTime(Date createTime) { 42 this.createTime = createTime; 43 } 44 45 @Override 46 public String toString() { 47 return "Video{" + 48 "name=‘" + name + ‘\‘‘ + 49 ", createTime=" + createTime + 50 ‘}‘; 51 } 52 }
1 /** 2 * @author it-小林 3 * @desc 4 * @date 2021年07月14日 20:16 5 */ 6 public class BCopy { 7 8 public static void main(String[] args) throws CloneNotSupportedException { 9 //原型对象v1 10 Date date = new Date(); 11 Video v1 = new Video("原型模式", date); 12 Video v2 = (Video) v1.clone(); 13 System.out.println("v1=>" + v1); 14 System.out.println("v2=>" + v2); 15 16 System.out.println("======================"); 17 date.setTime(2114422); 18 System.out.println("v1=>" + v1); 19 System.out.println("v2=>" + v2); 20 21 22 //v1 克隆v2 23 //Video v2 = new Video("原型模式", date); 24 /*Video v2 = (Video) v1.clone(); 25 System.out.println("v2=>" + v2); 26 System.out.println("v2=>hash:" + v2.hashCode()); 27 v2.setName("原型模式复制"); 28 System.out.println(v2);*/ 29 } 30 }
运行结果:
(2)深拷贝
1 /** 2 * @author it-小林 3 * @desc 4 * @date 2021年07月14日 20:08 5 * 步骤 6 * 1、实现一个接口 Cloneable 7 * 2、重写一个方法 clone() 8 */ 9 public class Video implements Cloneable { 10 11 private String name; 12 13 private Date createTime; 14 15 16 @Override 17 protected Object clone() throws CloneNotSupportedException { 18 Object obj = super.clone(); 19 20 Video v = (Video) obj; 21 v.createTime = (Date) this.createTime.clone(); 22 23 return v; 24 } 25 26 public Video() { 27 } 28 29 public Video(String name, Date createTime) { 30 this.name = name; 31 this.createTime = createTime; 32 } 33 34 public String getName() { 35 return name; 36 } 37 38 public void setName(String name) { 39 this.name = name; 40 } 41 42 public Date getCreateTime() { 43 return createTime; 44 } 45 46 public void setCreateTime(Date createTime) { 47 this.createTime = createTime; 48 } 49 50 @Override 51 public String toString() { 52 return "Video{" + 53 "name=‘" + name + ‘\‘‘ + 54 ", createTime=" + createTime + 55 ‘}‘; 56 } 57 }
1 /** 2 * @author it-小林 3 * @desc 4 * @date 2021年07月14日 20:16 5 * Spring Bean : 单例模式 原型模式 6 * 原型模式 + 工厂模式 ==》new + 工厂模式 7 * 8 * 9 */ 10 public class BCopy { 11 12 public static void main(String[] args) throws CloneNotSupportedException { 13 //原型对象v1 14 Date date = new Date(); 15 Video v1 = new Video("原型模式", date); 16 Video v2 = (Video) v1.clone(); 17 System.out.println("v1=>" + v1); 18 System.out.println("v2=>" + v2); 19 20 System.out.println("======================"); 21 date.setTime(2114422); 22 System.out.println("v1=>" + v1); 23 System.out.println("v2=>" + v2); 24 25 } 26 }
运行结果: