1. 概念
- 原型模式是一种创建型设计模式,它通过复制(克隆)一个现有的对象来创建新的对象,而不是通过调用构造函数的方式。
2. 原理结构图
- 原理结构图说明
- 原型接口(Prototype):定义了复制方法的接口,通常由 Cloneable 接口实现。
- 具体原型(ConcretePrototype):实现原型接口的类,提供具体的复制方法。
- 客户端(Client):使用具体原型类的实例来创建新的对象。
3. 代码示例
3.1 示例1
- 简单的Java原型模式代码示例
java
// 创建一个实现了Cloneable接口的抽象类
public abstract class Prototype implements Cloneable {
private String name;
public Prototype(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
// 重写clone()方法
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
// 创建一个具体的原型类
public class ConcretePrototype extends Prototype {
public ConcretePrototype(String name) {
super(name);
}
}
// 测试类
public class Test {
public static void main(String[] args) {
// 创建一个原型对象
Prototype prototype = new ConcretePrototype("原型对象");
// 使用clone()方法创建新对象
try {
Prototype clonedPrototype = (Prototype) prototype.clone();
System.out.println("原型对象:" + prototype.getName());
System.out.println("克隆对象:" + clonedPrototype.getName());
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
3.2 示例二
- 实例代码
java
abstract class BasketballPrototype {
protected String brand;
protected String desc;
protected double weight;
protected double perimeter;
public BasketballPrototype(String brand, String desc, double weight, double perimeter) {
this.brand = brand;
this.desc = desc;
this.weight = weight;
this.perimeter = perimeter;
}
@Override
public abstract BasketballPrototype clone();
}
class Spalding extends BasketballPrototype {
public Spalding(String brand, String desc, double weight, double perimeter) {
super(brand, desc, weight, perimeter);
}
@Override
public BasketballPrototype clone() {
System.out.printf("生产 Spalding: %s %s %s %s\n", this.brand, this.desc, this.weight, this.perimeter);
return new Spalding(this.brand, this.desc, this.weight, this.perimeter);
}
}
class Molten extends BasketballPrototype {
public Molten(String brand, String desc, double weight, double perimeter) {
super(brand, desc, weight, perimeter);
}
@Override
public BasketballPrototype clone() {
System.out.printf("生产 Molten: %s %s %s %s\n", this.brand, this.desc, this.weight, this.perimeter);
return new Molten(this.brand, this.desc, this.weight, this.perimeter);
}
}
class BasketballPrototypeManager {
private static Hashtable<String, BasketballPrototype> basketballPrototypeMap = new Hashtable<>();
public static BasketballPrototype get(String type) {
BasketballPrototype basketballPrototype = basketballPrototypeMap.get(type);
return basketballPrototype.clone();
}
public static void buildProtypeList() {
Spalding spalding = new Spalding("Spalding", "通用性比较强,适合室内外使用", 600, 75);
basketballPrototypeMap.put("Spalding", spalding);
Molten molten = new Molten("Molten", "独特的外观和优秀的性能", 610, 80);
basketballPrototypeMap.put("Molten", molten);
}
}
public class BasketballFactoryTest {
public static void main(String[] args) {
BasketballPrototypeManager.buildProtypeList();
for (int i = 0; i < 8; i++) {
BasketballPrototypeManager.get("Spalding");
BasketballPrototypeManager.get("Molten");
}
}
}
4. 优缺点
- 优点
- 性能提升:通过复制已有对象来创建新对象,通常比创建全新的对象更快。
- 减少数据库调用:可以缓存对象,在下一个请求时返回它的克隆,需要时更新数据库,从而减少数据库调用次数。
- 简化创建过程:不需要知道具体的类,只需要一个现有的实例就可以创建新对象。
- 动态加载:可以在运行时增加或减少属性,使得对象更加灵活。
- 缺点
- 深拷贝问题:如果对象中的字段是引用类型,简单的复制可能会导致所有克隆的对象共享相同的引用,这可能不是预期的行为。
- 内存占用:每个克隆的对象都会占用额外的内存,尤其是在大量克隆时,可能导致内存消耗过大。
- 实现复杂性:正确实现原型模式可能需要重写clone()方法,并处理复杂的引用关系,这可能会使代码变得复杂。
5. 应用场景
- 资源密集型类初始化:如果类的初始化过程消耗资源过多,例如加载大型位图或初始化复杂数据结构,使用原型模式可以避免每次创建新对象时都进行昂贵的初始化操作。
- 大量相似对象生成:当需要生成大量相似的对象时,原型模式可以通过复制现有对象来减少构造函数的调用次数,从而提高性能。
- 动态对象创建:在运行时需要根据不同情况创建不同类型的对象时,原型模式可以提供一种灵活的方式来实现对象的动态创建,而不需要预先定义所有可能的对象类型。
- 避免重复代码:当多个类具有相同的创建逻辑时,可以使用原型模式来避免重复编写相同的代码,而是通过克隆现有对象来实现。
- 自定义对象创建过程:如果需要在创建对象时加入特定的逻辑或者状态,原型模式允许在克隆过程中对这些逻辑或状态进行定制。
6. JDK中的使用
- 集合类框架:在Java的集合类框架中,例如ArrayList、HashMap等,当需要创建新的集合对象时,可以通过实现Cloneable接口并重写clone()方法来复制现有的集合对象,从而创建新的对象。
- 序列化机制:在Java的序列化机制中,通过序列化一个对象,可以创建一个该对象的副本。反序列化过程实际上就是通过已有的对象状态创建新对象的过程,这与原型模式的思想相符。
7. 注意事项
- 浅拷贝与深拷贝:理解浅拷贝和深拷贝的区别。浅拷贝只复制对象本身和其包含的基本类型字段,而深拷贝则递归地复制对象及其引用的所有对象。
- 避免循环引用:在实现深拷贝时,要注意避免对象间的循环引用,否则可能导致无限递归的问题。
- 性能考虑:深拷贝可能涉及复杂的对象图和多次复制操作,可能会对性能产生影响,因此需要根据具体情况权衡是否使用深拷贝。
- 安全性:在某些情况下,可能需要限制克隆操作,以保护对象的状态不被未授权的修改。可以通过将 clone() 方法声明为 protected 或 package-private 来实现。
- 客户端使用:客户端应通过具体原型类的 clone() 方法来创建新对象,而不是直接调用构造函数。