什么是原型模式
原型模式(Prototype Pattern)是指创建重复的对象,同时又能保证性能。它属于创建型模式,提供了一种快速创建对象的方法,而不必经过完整的构造过程。通常,在创建新的对象时,我们使用"new"关键字来实例化对象。但是,在某些情况下,该方法可能会因为创建成本高昂,时间长等原因,无法满足需求。此时,原型模式就派上用场了。它通过克隆原型对象来创建新的对象,从而避免了"new"关键字带来的问题。
原型模式的实现方式
原型模式的核心思想是:将一个原型对象作为被克隆的对象,在需要新对象时通过克隆原型对象获取新对象。Java语言中,原型模式的实现主要依靠Object类的clone()方法进行实现。
下面是一个简单的示例代码:
public abstract class Prototype implements Cloneable {
public abstract void print();
@Override
public Object clone() {
Object clone = null;
try {
clone = super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return clone;
}
}
public class ConcretePrototype extends Prototype {
private String name;
public ConcretePrototype(String name) {
this.name = name;
}
@Override
public void print() {
System.out.println("ConcretePrototype Name:" + name);
}
}
public class Client {
public static void main(String[] args) {
ConcretePrototype prototype1 = new ConcretePrototype("prototype1");
ConcretePrototype prototype2 = (ConcretePrototype) prototype1.clone();
System.out.println("Prototype1 HashCode:" + prototype1.hashCode());
System.out.println("Prototype2 HashCode:" + prototype2.hashCode());
prototype2.print();
}
}
在上面的示例代码中,我们定义了一个抽象类Prototype作为原型类,并在其中定义了一个抽象方法print()和一个clone()方法。具体的实现类ConcretePrototype继承自原型类,并重写了print()方法。Client类则用来测试原型模式的效果。
原型模式的优缺点
原型模式的优点包括:
- 提高了系统的性能。 在创建新对象时,原型模式通过克隆原有对象的方式避免了大量的重复构造工作。这样可以大大提高系统的性能和运行速度。
- 简化了对象的创建过程。 相比于其他创建型模式,原型模式的对象创建过程非常简单,只需要通过克隆原型对象即可获得新的对象,无需过多的构造工作。
- 可以动态地增加或减少产品类。 原型模式可以动态地增加或减少产品类,使得系统更加灵活。
其缺点包括:
- 需要额外的注意事项。 在使用原型模式时,需要注意克隆对象的深度和浅度,避免出现意外情况。
- 克隆出来的对象不会去执行构造函数。 使用原型模式克隆出来的新对象并不会执行构造函数,这在某些情况下可能会导致问题。
原型模式在Java中的应用
原型模式在Java中的应用非常广泛。例如,Java中的java.util.Date就是一个典型的原型模式。另外,在实际开发中,我们也可以把一些常用的对象作为原型,根据需求进行克隆来提高效率。
下面给出一个具体的例子:假设我们要设计一个类库,其中包含许多图形对象(如圆、矩形、三角形等)。由于这些图形对象都具有相似的属性和方法,因此我们可以使用原型模式来实现这个类库。
首先,我们定义一个图形接口:
public interface Shape extends Cloneable {
void draw();
}
然后,我们分别实现圆、矩形、三角形等图形类,并实现clone()方法:
public class Circle implements Shape {
public Circle(){
System.out.println("Circle Constructor Called!");
}
@Override
public void draw() {
System.out.println("Inside Circle::draw() method.");
}
@Override
public Object clone() {
Object clone = null;
try {
clone = super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return clone;
}
}
public class Rectangle implements Shape {
public Rectangle(){
System.out.println("Rectangle Constructor Called!");
}
@Override
public void draw() {
System.out.println("Inside Rectangle::draw() method.");
}
@Override
public Object clone() {
Object clone = null;
try {
clone = super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return clone;
}
}
public class Triangle implements Shape {
public Triangle(){
System.out.println("Triangle Constructor Called!");
}
@Override
public void draw() {
System.out.println("Inside Triangle::draw() method.");
}
@Override
public Object clone() {
Object clone = null;
try {
clone = super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return clone;
}
}
最后,我们定义一个ShapeCache类来缓存这些图形对象:
import java.util.HashMap;
import java.util.Map;
public class ShapeCache {
private static Map<String, Shape> shapeMap = new HashMap<String, Shape>();
public static Shape getShape(String shapeId) {
Shape cachedShape = shapeMap.get(shapeId);
return (Shape) cachedShape.clone();
}
public static void loadCache() {
Circle circle = new Circle();
circle.setId("1");
shapeMap.put(circle.getId(),circle);
Rectangle rectangle = new Rectangle();
rectangle.setId("2");
shapeMap.put(rectangle.getId(),rectangle);
Triangle triangle = new Triangle();
triangle.setId("3");
shapeMap.put(triangle.getId(),triangle);
}
}
在上面的代码中,我们将不同类型的图形对象存储在一个HashMap中,并通过getShape()方法获取它们的克隆对象。loadCache()方法用来初始化这些图形对象。
下面是测试代码:
public class Client {
public static void main(String[] args) {
ShapeCache.loadCache();
Shape clonedShape1 = (Shape) ShapeCache.getShape("1");
System.out.println("Shape : " + clonedShape1.getType());
Shape clonedShape2 = (Shape) ShapeCache.getShape("2");
System.out.println("Shape : " + clonedShape2.getType());
Shape clonedShape3 = (Shape) ShapeCache.getShape("3");
System.out.println("Shape : " + clonedShape3.getType());
}
}
在上面的代码中,我们通过getShape()方法获取了三种不同类型的图形对象,并打印出它们的类型。
如何在项目中使用原型模式
在实际项目中,我们可以使用原型模式来解决以下问题:
- 创建新对象的过程比较复杂,需要耗费很多时间和资源。
- 需要创建大量相似或完全相同的对象。
例如,在游戏开发中,经常需要创建大量的怪物、宝箱等对象。使用原型模式可以避免重复构造这些对象,提高游戏运行效率。另外,在一些需要频繁修改或调整的系统中,原型模式也可以提高系统的灵活性。
原型模式的变形
除了普通的原型模式,还有一些基于原型模式的变形模式,如浅拷贝、深拷贝等。其中,浅拷贝只会复制对象的基本属性,而不会复制嵌套对象;深拷贝则会复制整个对象的所有属性,包括嵌套对象和数组等。以下是一个简单的示例代码:
public class Person implements Cloneable {
private String name;
private Address address;
public Person(String name, Address address) {
this.name = name;
this.address = address;
}
public void setAddress(String street, String city, String state) {
address.setStreet(street);
address.setCity(city);
address.setState(state);
}
public void print() {
System.out.println("Name: " + name + ", Address: " + address);
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class Address implements Cloneable {
private String street;
private String city;
private String state;
public Address(String street, String city, String state) {
this.street = street;
this.city = city;
this.state = state;
}
public void setStreet(String street) {
this.street = street;
}
public void setCity(String city) {
this.city = city;
}
public void setState(String state) {
this.state = state;
}
@Override
public String toString() {
return street + ", " + city + ", " + state;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
在上面的代码中,我们定义了一个Person类和一个Address类,并实现了它们的clone()方法。Person类包含一个嵌套的Address对象,用来演示浅拷贝和深拷贝的区别。
以下是测试代码:
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
Address address = new Address("Main St", "New York", "NY");
Person person1 = new Person("John", address);
person1.print();
// 浅拷贝
Person person2 = (Person) person1.clone();
// 修改person2的地址信息,会影响到person1的地址信息
person2.setAddress("2nd Ave", "Chicago", "IL");
person1.print();
person2.print();
// 深拷贝
Address address2 = (Address) address.clone();
Person person3 = new Person("Mary", address2);
// 修改person3的地址信息,不会影响到address和person1的地址信息
person3.setAddress("3rd St", "Los Angeles", "CA");
person1.print();
person3.print();
}
}
在上面的代码中,我们首先创建了一个Person对象,并打印出它的详细信息。然后,使用浅拷贝和深拷贝分别复制了这个Person对象,并修改了复制后对象的地址信息。最后,打印出所有对象的详细信息。
总结
原型模式是一种简单而实用的创建型模式,它可以帮助我们快速创建大量相似的对象,提高系统的性能和灵活性。通过基于已有对象进行克隆,可以避免重复构造对象的过程,同时还可以简化对象的创建过程。
在使用原型模式时,需要注意几点:
- 原型对象必须实现Cloneable接口,否则无法进行克隆。
- 克隆方法clone()应该被重写,并且需要调用父类的clone()方法。
- 在使用克隆对象时,需要注意深拷贝和浅拷贝的区别。
除了普通的原型模式,还有一些变形模式,如浅拷贝、深拷贝等,可以根据具体需求选择合适的模式。
最后,需要注意的是,在Java中,克隆对象并不总是比直接创建对象更快。如果对象的构造过程很简单,并且只需要创建少量对象,那么直接创建对象可能更加简单和高效。