原型设计模式
原型模式(Prototype Pattern)是一种创建型设计模式,其主要目的是通过复制(克隆)现有对象来创建新对象,而无需显式地使用构造函数创建新对象。这种模式通常用于创建成本较高或复杂的对象,以避免重复的初始化工作。
结构
- 原型接口(Prototype Interface):通常需要创建一个原型接口,该接口定义了一个
clone
方法,用于复制对象。所有需要支持克隆操作的类都需要实现这个接口。 - 具体原型(Concrete Prototype):具体原型类是实现了原型接口的类,它实际上进行对象的复制操作。具体原型类需要实现
clone
方法来克隆自身。 - 客户端(Client):客户端代码通过原型接口来请求克隆操作(即使用具体原型类中的clone()方法来复制新对象),而不需要知道具体的对象创建细节。
实现
原型模式可以分为浅克隆和深克隆两种形式,取决于复制的方式。
- 浅克隆:创建一个新对象,新对象的属性与原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。
- 深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有的对象地址。
Java中的Object类中提供了clone()方法来实现浅克隆。
class Prototype implements Cloneable{public Prototype() {System.out.println("创建具体的原型对象");}@Overrideprotected Prototype clone() throws CloneNotSupportedException {System.out.println("复制成功!");return (Prototype) super.clone();}
}public class PrototypeDemo {public static void main(String[] args) throws CloneNotSupportedException{Prototype original = new Prototype();Prototype clone = original.clone();System.out.println(original == clone); //false}
}
上面的方法很简单,当然,还有另外一种方法,就是自己创建原型接口,按照上面的原型模式的结构来实现
// 原型接口
interface Prototype {Prototype clone();
}// 具体原型类
class ConcretePrototype implements Prototype {private String data;public ConcretePrototype(String data) {this.data = data;}@Overridepublic Prototype clone() {return new ConcretePrototype(this.data);}public String getData() {return data;}
}public class PrototypeDemo {public static void main(String[] args) {// 创建一个具体原型对象Prototype original = new ConcretePrototype("Hello, world!");// 克隆原型对象来创建新对象Prototype clone = original.clone();// 输出原型和克隆对象的数据System.out.println("Original Data: " + ((ConcretePrototype) original).getData());System.out.println("Clone Data: " + ((ConcretePrototype) clone).getData());}
}
使用场景
原型模式使用场景有:
-
对象的创建成本较高:如果创建一个对象的成本很高,例如需要从数据库中加载大量数据或进行复杂的计算,那么使用原型模式可以避免多次创建相同的对象,而是通过克隆已有对象来创建新对象,从而提高性能。
-
对象的创建过程复杂:当对象的创建过程非常复杂,包括多个步骤或依赖于其他对象时,使用原型模式可以简化代码,因为您只需克隆一个已有对象,而不必重复执行复杂的创建步骤。
-
需要保持对象的不变性:有些对象需要保持不变性,即不能通过直接赋值属性的方式修改其状态。原型模式可以帮助您创建对象的副本,而不会影响原始对象的状态。
-
大量相似对象的创建:当需要创建大量相似但略有差异的对象时,原型模式非常有用。您可以创建一个原型对象,然后根据需要克隆该对象并进行适当的修改。
-
支持动态配置对象:原型模式允许您在运行时动态配置对象,通过克隆已有对象并进行修改,而不必硬编码不同的配置选项。
-
减少子类的创建:在某些情况下,如果您有多个子类,而这些子类只有一些差异,可以使用原型模式来创建这些子类的实例,而不必为每个子类都创建一个独立的类。
深克隆
在实现深克隆时,有几种常见的方式:
- 手动实现深克隆:这是最基本的方式,开发人员需要手动编写代码来遍历原始对象的属性和子对象,并创建相应的新对象来存储复制后的数据。这通常涉及递归操作,以确保所有嵌套对象也被深克隆。
class Address implements Cloneable {private String street;private String city;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;}public Address(String street, String city) {this.street = street;this.city = city;}@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}class Person implements Cloneable {public String getName() {return name;}public void setName(String name) {this.name = name;}public Address getAddress() {return address;}public void setAddress(Address address) {this.address = address;}public Person(String name, Address address) {this.name = name;this.address = address;}private String name;private Address address;@Overrideprotected Object clone() throws CloneNotSupportedException {Person clonedPerson = (Person) super.clone();clonedPerson.address = (Address) address.clone();return clonedPerson;}
}public class Main {public static void main(String[] args) throws CloneNotSupportedException {Address address = new Address("步行街", "北京");Person originalPerson = new Person("Alice", address);Person clonedPerson = (Person) originalPerson.clone();System.out.println(originalPerson.getName()+" "+originalPerson.getAddress().getStreet()); // Alice 步行街clonedPerson.setName("Bob");clonedPerson.getAddress().setStreet("人民公园");System.out.println(clonedPerson.getName()+" "+clonedPerson.getAddress().getStreet()); // Bob 人民公园}
}
- 使用序列化和反序列化:这种方式需要将原始对象序列化为一个字节流,然后再将其反序列化为新对象。这可以通过语言内置的序列化机制或第三方库来实现。在这个过程中,对象的所有属性和嵌套对象都会被复制。
import java.io.*;class Address implements Serializable {private String street;private String city;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;}public Address(String street, String city) {this.street = street;this.city = city;}@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}class Person implements Serializable {public String getName() {return name;}public void setName(String name) {this.name = name;}public Address getAddress() {return address;}public void setAddress(Address address) {this.address = address;}public Person(String name, Address address) {this.name = name;this.address = address;}private String name;private Address address;@Overrideprotected Object clone() throws CloneNotSupportedException {Person clonedPerson = (Person) super.clone();clonedPerson.address = (Address) address.clone();return clonedPerson;}
}public class Main {public static void main(String[] args) throws IOException, ClassNotFoundException {Address address = new Address("步行街", "北京");Person originalPerson = new Person("Alice", address);// 序列化主类ByteArrayOutputStream baos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(baos);oos.writeObject(originalPerson);// 反序列化创造克隆类ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());ObjectInputStream ois = new ObjectInputStream(bais);Person clonedPerson = (Person) ois.readObject();System.out.println(originalPerson.getName()+" "+originalPerson.getAddress().getStreet()); // Alice 步行街clonedPerson.setName("Bob");clonedPerson.getAddress().setStreet("人民公园");System.out.println(clonedPerson.getName()+" "+clonedPerson.getAddress().getStreet()); // Bob 人民公园}
}