原型模式是一种创建型模式,它的工作原理很简单,将一个对象克隆给另一个需要使用它的对象,这里通过克隆方法创建出来的对象是一个全新的对象,他们在内存中拥有新的地址,通常对克隆所产生的对象进行修改不会对原型对象造成任何影响。
原型模式的结构
**1.prototype(抽象原型类):**它是声明克隆方法的接口,是所有具体原型类的公共父类,它可以是抽象类,也可以是抽象接口,甚至还可以是具体实现类
**2.ConcretePrototype(具体原型类):**它实现在抽象原型类中声明的克隆方法,在克隆方法中返回自己的一个克隆对象
**3.Client(客户类):**在客户类中,让一个原型对象克隆自身从而创建一个新的对象,只需要直接实例化或通过工厂方法等方式创建一个新的对象,再通过调用该对象的克隆方法可以得到多个相同的对象
这里不得不谈到深克隆和浅克隆的概念了。
浅克隆
package com.designpatten.prototype.demo1;
import java.io.*;
public class Student implements Cloneable,Serializable{
private String username;
private String password;
private Address address;
// 浅拷贝
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();//这个应该是浅拷贝,先试试
}
@Override
public String toString() {
return "Student{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
", address=" + address +
'}';
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
package com.designpatten.prototype.demo1;
import java.io.Serializable;
//学生类里面的地址信息
public class Address implements Cloneable, Serializable {
private String province;
private String city;
private String country;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "Address{" +
"province='" + province + '\'' +
", city='" + city + '\'' +
", country='" + country + '\'' +
'}';
}
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
}
package com.designpatten.prototype.demo1;
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
Student student = new Student();
student.setAddress(new Address());
student.setUsername("张三");
student.setPassword("123123");
Student student1 = (Student) student.clone();
System.out.println(student.hashCode());
System.out.println(student1.hashCode());
//这里就体现出来了,对象中的引用类型只是复制了指针,并没有真正的拷贝,所以这个是浅拷贝
System.out.println(student.getAddress().hashCode());
System.out.println(student1.getAddress().hashCode());
//这里再来一个深拷贝,用序列化的方式A
}
}
运行结果
这里可以很明显的看出来,通过克隆方法获得的对象,那个对象是一个内容和原对象相同的另一个对象,但是对象中使用到的引用类型并不会拷贝一份,而只是拷贝了一份引用,这个就是浅拷贝
深拷贝
package com.designpatten.prototype.demo1;
import java.io.Serializable;
//学生类里面的地址信息
public class Address implements Cloneable, Serializable {
private String province;
private String city;
private String country;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "Address{" +
"province='" + province + '\'' +
", city='" + city + '\'' +
", country='" + country + '\'' +
'}';
}
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
}
package com.designpatten.prototype.demo1;
import java.io.*;
public class Student implements Cloneable,Serializable{
private String username;
private String password;
private Address address;
//深拷贝
@Override
protected Object clone() throws CloneNotSupportedException {
ByteArrayOutputStream baos = null;
ObjectOutputStream oos = null;
ByteArrayInputStream bios = null;
ObjectInputStream ois = null;
try {
baos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(baos);
oos.writeObject(this); //将这个对象以对象流的方式写出
//然后再创建一个输入流,读取新的,最后返回
bios = new ByteArrayInputStream(baos.toByteArray());
ois = new ObjectInputStream(bios);
return ois.readObject();
} catch (Exception e) {
e.printStackTrace();
}finally {
if (baos != null){
try {
baos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (oos != null){
try {
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (ois != null){
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
@Override
public String toString() {
return "Student{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
", address=" + address +
'}';
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
package com.designpatten.prototype.demo1;
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
Student student = new Student();
student.setAddress(new Address());
student.setUsername("张三");
student.setPassword("123123");
Student student1 = (Student) student.clone();
System.out.println("student对象的hashCode");
System.out.println(student.hashCode());
System.out.println(student1.hashCode());
//这里就体现出来了,对象中的引用类型只是复制了指针,并没有真正的拷贝,所以这个是浅拷贝
System.out.println("student对象中的address的hashCode");
System.out.println(student.getAddress().hashCode());
System.out.println(student1.getAddress().hashCode());
//这里再来一个深拷贝,用序列化的方式A
}
}
运行结果
这里使用对象流的方式进行拷贝,最后clone出来的对象,无论是对象本身还是它内部的引用类型,都被克隆了一份,这个就是深拷贝
原型模式的优点
1.当创建新的对象实例较为复杂的时候,使用原型模式可以简化对象的创建过程,通过复制一个已有的实例可以提高新实例的创建效率
2.扩展性较好,由于再原型模式中提供了抽象原型类,在客户端可以针对抽象原型类进行编程,而将具体原型类写在配置文件中,增加或减少产品类对原有系统没有任何影响
3.原型模式提供了简化的创建结构,工厂方法模式需要有一个与产品类等级结构相同的工厂等级结构,而原型模式就不需要这样,原型模式中产品的复制时通过封装在原型类中的克隆方法实现的
4.可以使用深克隆的方式保存对象的状态,使用原型模式将对象复制一份并将其状态保存起来,以便在需要的时候使用
原型模式的缺点
1.需要为每一个类配备一个克隆方法,而且该克隆方法位于一个类的内部,当对已有的类进行改造时需要修改源代码,违背了开闭原则