原型模式(Prototype Pattern)是一种创建型设计模式,允许你复制现有对象而不必深入理解其复杂结构。它通过复制原型对象来创建新对象,新对象是原型对象的一个精确副本,包括其内部状态。
概念与作用
原型模式的核心概念是“原型”,即一个具有某种特定状态的对象,通过复制这个对象来创建新的对象。
其作用主要体现在以下几个方面:
1.简化对象创建过程
无需通过new关键字以及一系列初始化操作来创建复杂对象,只需通过复制已有对象即可得到一个新对象,避免了繁琐的对象创建过程。
2.性能优化
在某些情况下,创建新对象的成本较高,如需要进行大量的计算或数据访问等。而原型模式通过复制已有对象,可以避免这些高成本的操作,提高系统的性能。
3.灵活的对象创建方式
用户可以根据实际需求动态地指定要创建的对象类型,通过选择不同的原型对象进行复制,实现灵活的对象创建。
举例
场景:简历创建系统
假设我们有一个简历创建系统,用户可以创建自己的简历,并且希望能够快速地复制一份简历作为基础,然后进行修改。这里就可以使用原型模式。
// 简历类,实现Cloneable接口
class Resume implements Cloneable {
private String name; // 姓名
private String gender; // 性别
private int age; // 年龄
private WorkExperience workExperience; // 工作经历
// 构造方法
public Resume(String name, String gender, int age, WorkExperience workExperience) {
this.name = name;
this.gender = gender;
this.age = age;
this.workExperience = workExperience;
}
// 实现clone方法,深拷贝
@Override
protected Resume clone() {
// 深拷贝:复制工作经历对象
WorkExperience clonedWorkExperience = new WorkExperience(this.workExperience.getCompany(), this.workExperience.getWorkYears());
return new Resume(this.name, this.gender, this.age, clonedWorkExperience);
}
// getter和setter方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public WorkExperience getWorkExperience() {
return workExperience;
}
public void setWorkExperience(WorkExperience workExperience) {
this.workExperience = workExperience;
}
@Override
public String toString() {
return "Resume{" +
"name='" + name + '\'' +
", gender='" + gender + '\'' +
", age=" + age +
", workExperience=" + workExperience +
'}';
}
}
// 工作经历类
class WorkExperience {
private String company; // 公司名称
private int workYears; // 工作年限
// 构造方法
public WorkExperience(String company, int workYears) {
this.company = company;
this.workYears = workYears;
}
// getter和setter方法
public String getCompany() {
return company;
}
public void setCompany(String company) {
this.company = company;
}
public int getWorkYears() {
return workYears;
}
public void setWorkYears(int workYears) {
this.workYears = workYears;
}
@Override
public String toString() {
return "WorkExperience{" +
"company='" + company + '\'' +
", workYears=" + workYears +
'}';
}
}
// 测试类
public class PrototypePatternDemo {
public static void main(String[] args) {
// 创建原始简历对象
WorkExperience workExperience = new WorkExperience("ABC公司", 3);
Resume originalResume = new Resume("张三", "男", 28, workExperience);
System.out.println("原始简历:" + originalResume);
// 复制简历对象,这里是直接使用已存在的对象,而不是新建对象
Resume clonedResume = originalResume.clone();
clonedResume.setName("李四");
clonedResume.getWorkExperience().setCompany("DEF公司");
System.out.println("复制后的简历:" + clonedResume);
}
}
原始简历:Resume{name='张三', gender='男', age=28, workExperience=WorkExperience{company='ABC公司', workYears=3}}
复制后的简历:Resume{name='李四', gender='男', age=28, workExperience=WorkExperience{company='DEF公司', workYears=3}}
不使用原型模式的短板
如果不使用原型模式,直接通过new关键字创建对象,可能会存在以下问题:
1.重复初始化问题
如果对象的创建过程比较复杂,如需要进行大量的计算或数据访问等,那么每次创建新都需要对象重复这些初始化操作,导致系统性能下降。
2.无法灵活创建对象
无法根据现有对象的状态快速创建新对象,需要重新指定所有属性的值,不够灵活。
3.代码可维护性差
如果对象的结构发生变化,如增加新的属性或修改属性的类型等,那么所有创建该对象的地方都需要进行相应的修改,增加了代码的维护成本。
例如,在上述简历创建系统的场景中,如果不使用原型模式,每次创建新简历都需要重新输入姓名、性别、年龄以及工作经历等信息,无法快速地基于已有简历进行修改。而且,如果简历的结构发生变化,如增加教育背景等属性,那么所有创建简历的地方都需要修改,增加了代码的维护难度。
实现条件
在Java中,使用原型模式时,通常需要满足以下条件:
1.实现Cloneable
接口
Cloneable
是一个标记接口,没有需要实现的方法。但它用于表明该类的对象可以被复制。如果一个类没有实现Cloneable
接口,但却调用了clone()
方法,会抛出CloneNotSupportedException
异常。
2. 重写clone()
方法
clone()
方法是Object
类的一个protected方法。为了能够在外部分使用并定制复制逻辑,通常需要将其重写为public方法,并在其中实现具体的复制逻辑。
3. 深拷贝与浅拷贝的考虑
如果对象包含其他对象的引用(即组合对象),则需要考虑是进行浅拷贝还是深拷贝:
浅拷贝:只复制对象本身,不会复制其内部引用的对象,复制后的对象和原对象仍然引用相同的内部对象。
深拷贝:不仅复制对象本身,还会复制其内部引用的所有对象,确保复制后的对象与原对象完全独立。
在实际应用中,需要根据具体需求选择合适的拷贝方式。
深拷贝与浅拷贝
在对象复制过程中,浅拷贝和深拷贝是两种不同的复制方式,它们在处理对象的内部引用时有着本质的区别。
浅拷贝
浅拷贝只复制对象本身,不会复制其内部引用的对象。复制后的对象与原对象仍然引用相同的内部对象。
class Address {
private String street;
private String city;
public Address(String street, String city) {
this.street = street;
this.city = city;
}
// getter和setter方法
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
@Override
public String toString() {
return "Address{" +
"street='" + street + '\'' +
", city='" + city + '\'' +
'}';
}
}
class Person implements Cloneable {
private String name;
private int age;
private Address address;
public Person(String name, int age, Address address) {
this.name = name;
this.age = age;
this.address = address;
}
// 实现clone方法,浅拷贝
@Override
protected Person clone() {
return new Person(this.name, this.age, this.address);
}
// getter和setter方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", address=" + address +
'}';
}
}
public class ShallowCopyDemo {
public static void main(String[] args) {
// 创建原始对象
Address originalAddress = new Address("Main Street", "New York");
Person originalPerson = new Person("Alice", 25, originalAddress);
System.out.println("原始对象:" + originalPerson);
// 浅拷贝
Person clonedPerson = originalPerson.clone();
System.out.println("浅拷贝后的对象:" + clonedPerson);
// 修改原始对象的地址
originalPerson.getAddress().setStreet("Broadway");
System.out.println("修改原始对象地址后,原始对象:" + originalPerson);
System.out.println("修改原始对象地址后,拷贝对象:" + clonedPerson);
}
}
原始对象:Person{name='Alice', age=25, address=Address{street='Main Street', city='New York'}}
浅拷贝后的对象:Person{name='Alice', age=25, address=Address{street='Main Street', city='New York'}}
修改原始对象地址后,原始对象:Person{name='Alice', age=25, address=Address{street='Broadway', city='New York'}}
修改原始对象地址后,拷贝对象:Person{name='Alice', age=25, address=Address{street='Broadway', city='New York'}}
从输出结果可以看出,浅拷贝后的对象与原始对象共享相同的Address
对象。当修改原始对象的地址时,拷贝对象的地址也随之改变。
深拷贝
深拷贝不仅复制对象本身,还会复制其内部引用的所有对象,确保复制后的对象与原对象完全独立。
class Address {
private String street;
private String city;
public Address(String street, String city) {
this.street = street;
this.city = city;
}
// 深拷贝方法(浅拷贝没有这个方法)
public Address deepCopy() {
return new Address(this.street, this.city);
}
// getter和setter方法
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
@Override
public String toString() {
return "Address{" +
"street='" + street + '\'' +
", city='" + city + '\'' +
'}';
}
}
class Person implements Cloneable {
private String name;
private int age;
private Address address;
public Person(String name, int age, Address address) {
this.name = name;
this.age = age;
this.address = address;
}
// 实现clone方法,深拷贝
@Override
protected Person clone() {
// 深拷贝地址对象,浅拷贝没有这个
Address clonedAddress = this.address.deepCopy();
return new Person(this.name, this.age, clonedAddress);
}
// getter和setter方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", address=" + address +
'}';
}
}
public class DeepCopyDemo {
public static void main(String[] args) {
// 创建原始对象
Address originalAddress = new Address("Main Street", "New York");
Person originalPerson = new Person("Alice", 25, originalAddress);
System.out.println("原始对象:" + originalPerson);
// 深拷贝
Person clonedPerson = originalPerson.clone();
System.out.println("深拷贝后的对象:" + clonedPerson);
// 修改原始对象的地址
originalPerson.getAddress().setStreet("Broadway");
System.out.println("修改原始对象地址后,原始对象:" + originalPerson);
System.out.println("修改原始对象地址后,拷贝对象:" + clonedPerson);
}
}
原始对象:Person{name='Alice', age=25, address=Address{street='Main Street', city='New York'}}
深拷贝后的对象:Person{name='Alice', age=25, address=Address{street='Main Street', city='New York'}}
修改原始对象地址后,原始对象:Person{name='Alice', age=25, address=Address{street='Broadway', city='New York'}}
修改原始对象地址后,拷贝对象:Person{name='Alice', age=25, address=Address{street='Main Street', city='New York'}}
从输出结果可以看出,深拷贝后的对象与原始对象是完全独立的。当修改原始对象的地址时,拷贝对象的地址保持不变。