原型模式属于对象的创建模式。通过给出一个原型对象来指明所有创建的对象的类型,然后用复制这个原型对象的办法创建出更多同类型的对象。
原型模式要求对象实现一个可以“克隆”自身的接口,这样就可以通过复制一个实例对象本身来创建一个新的实例。这样一来,通过原型实例创建新的对象,就不再需要关心这个实例本身的类型,只要实现了克隆自身的方法,就可以通过这个方法来获取新的对象,而无须再去通过new来创建。
1)、UML关系图
Java描述设计模式(05):原型模式
2)、核心角色
这种形式涉及到三个角色:
2)、抽象原型(Prototype)角色:这是一个抽象角色,通常由一个Java接口或Java抽象类实现。此角色给出所有的具体原型类所需的接口。 3)、具体原型(Concrete Prototype)角色:被复制的对象。此角色需要实现抽象的原型角色所要求的接口。
3)、基于JDK源码实现
/** * 基于JDK源码方式实现原型模式 */ public class C01_Property { public static void main(String[] args) { Product product = new Product("机械键盘","白色",100.00) ; Product product1 = (Product) product.clone(); Product product2 = (Product) product.clone(); System.out.println(product1); System.out.println(product2); System.out.println(product1==product2); // false } } class Product implements Cloneable { private String name ; private String color ; private Double price ; public Product(String name, String color, Double price) { this.name = name; this.color = color; this.price = price; } @Override public String toString() { return "Product{" + "name='" + name + '/'' + ", color='" + color + '/'' + ", price=" + price + '}'; } @Override protected Object clone() { Product product = null ; try{ product = (Product)super.clone() ; } catch (Exception e){ e.printStackTrace(); } return product ; } // 省略GET和SET方法 }
<!-- 多例Bean --> <bean id="sheep01" class="com.model.design.spring.node05.property.Sheep" scope="prototype" /> <!-- 单例Bean 默认: scope="singleton" --> <bean id="sheep02" class="com.model.design.spring.node05.property.Sheep"/>
@Test public void test01 (){ ApplicationContext context01 = new ClassPathXmlApplicationContext( "/spring/spring-property.xml"); // 原型模式 Sheep sheep1 = (Sheep)context01.getBean("sheep01") ; Sheep sheep2 = (Sheep)context01.getBean("sheep01") ; System.out.println(sheep1==sheep2); // false // 单例模式 Sheep sheep3 = (Sheep)context01.getBean("sheep02") ; Sheep sheep4 = (Sheep)context01.getBean("sheep02") ; System.out.println(sheep3==sheep4); // true }
1)、执行流程
if (mbd.isSingleton()) { sharedInstance = this.getSingleton(beanName, new ObjectFactory<Object>() { public Object getObject() throws BeansException { try { return AbstractBeanFactory.this.createBean(beanName, mbd, args); } catch (BeansException var2) { AbstractBeanFactory.this.destroySingleton(beanName); throw var2; } } }); bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } else if (mbd.isPrototype()) { var11 = null; Object prototypeInstance; try { this.beforePrototypeCreation(beanName); prototypeInstance = this.createBean(beanName, mbd, args); } finally { this.afterPrototypeCreation(beanName); } bean = this.getObjectForBeanInstance(prototypeInstance, name, beanName, mbd); }
2)、单例多例判断
所以默认就是单例模式,指定[scope="prototype"]就是原型模式。
public boolean isSingleton() { return "singleton".equals(this.scope) || "".equals(this.scope); } public boolean isPrototype() { return "prototype".equals(this.scope); }
1) 数据类型是基本数据类型、String类型的成员变量,浅拷贝直接进行值传递,也就是将该属性值复制一份给新的对象。
2) 数据类型是引用数据类型的成员变量,比如说成员变量是数组、类的对象等,浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。实际上两个对象的成员变量都指向同一个实例。修改其中一个对象属性会影响到另一个对象的属性。
3) 浅拷贝是使用默认的 clone()方法来实现。
1)、概念描述
除了浅拷贝要拷贝的值外,还负责拷贝引用类型的数据。那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象,这种对被引用到的对象的复制叫做间接复制。
2)、源代码实现
序列化实现深度克隆
在Java语言里深度克隆一个对象,常常可以先使对象实现Serializable接口,然后把对象(实际上只是对象的拷贝)写到一个流里(序列化),再从流里读回来(反序列化),便可以重建对象。
/** * 深拷贝和浅拷贝对比案例 */ public class C02_DeepClone { public static void main(String[] args) throws Exception { Dog dog = new Dog("Tom") ; Dog dog1 = (Dog)dog.clone() ; Dog dog2 = (Dog)dog.clone() ; // dog1:1639622804;dog2:1639622804 System.out.println("dog1:"+dog1.cat.hashCode()+";dog2:"+dog2.cat.hashCode()); Dog dog3 = (Dog)dog.deepClone() ; Dog dog4 = (Dog)dog.deepClone() ; // dog3:1937348256;dog4:1641808846 System.out.println("dog3:"+dog3.cat.hashCode()+";dog4:"+dog4.cat.hashCode()); } } class Cat implements Serializable { public String name ; public Cat (String name){ this.name = name ; } } class Dog implements Cloneable,Serializable { public String name ; public Cat cat ; public Dog (String name){ this.name = name ; this.cat = new Cat("Kit") ; } @Override protected Object clone() { Dog dog = null ; try{ dog = (Dog)super.clone() ; } catch (Exception e){ e.printStackTrace(); } return dog ; } public Object deepClone() throws IOException, ClassNotFoundException{ //将对象写到流里面:序列化 ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(this); //从流里面读出对象:反序列化 ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); return ois.readObject(); } }
原型模式允许在运行时动态改变具体的实现类型。原型模式可以在运行期间,由客户来注册符合原型接口的实现类型,也可以动态地改变具体的实现类型,看起来接口没有任何变化,但其实运行的已经是另外一个类实例了。因为克隆一个原型就类似于实例化一个类。
原型模式最主要的缺点是每一个类都必须配备一个克隆方法。配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类来说不是很难,而对于已经有的类不一定很容易,特别是当一个类引用不支持序列化的间接对象,或者引用含有循环结构的时候。
GitHub地址:知了一笑 https://github.com/cicadasmile 码云地址:知了一笑 https://gitee.com/cicadasmile