“在编程的世界里,最痛苦的事情莫过于重复造轮子。而原型模式,就是那个让你按下Ctrl+C、Ctrl+V的魔法!”
想象一下,你在玩乐高积木,花了2小时搭建了一个超酷的城堡。这时朋友来了也想要一个一模一样的,你会怎么办?
原型模式就是这个道理:通过复制现有对象来创建新对象,而不是重新构造。
生活中处处都有"克隆"的需求,编程也一样:
1. 对象创建成本很高
// 比如这个复杂的游戏角色创建
public class GameCharacter {
private String name;
private List<Skill> skills; // 需要从数据库加载
private Equipment equipment; // 需要复杂计算
private Map<String, Integer> attributes; // 需要AI算法生成
// 构造函数超级复杂,耗时很长
public GameCharacter(String name) {
this.name = name;
this.skills = loadSkillsFromDatabase(); // 数据库查询 500ms
this.equipment = calculateBestEquipment(); // 复杂计算 300ms
this.attributes = generateByAI(); // AI计算 1000ms
// 总共创建一个角色需要1.8秒!
}
}
2. 需要创建大量相似对象
// 文档模板场景
Document template = createComplexTemplate(); // 创建一次模板
Document doc1 = template.clone(); // 快速克隆
Document doc2 = template.clone(); // 快速克隆
Document doc3 = template.clone(); // 快速克隆
3. 对象状态多变,需要保存快照
// 游戏存档场景
GameState currentState = game.getCurrentState();
GameState savePoint = currentState.clone(); // 保存游戏进度
4. 数据库查询结果缓存
// 用户信息缓存
User userTemplate = userDao.findById(123); // 数据库查询
User user1 = userTemplate.clone(); // 快速克隆,避免重复查DB
User user2 = userTemplate.clone();
Java提供了Cloneable
接口和clone()
方法来支持原型模式:
/**
* 原型模式基础实现
* 实现Cloneable接口,重写clone方法
*/
public class Prototype implements Cloneable {
private String name;
private int age;
public Prototype(String name, int age) {
this.name = name;
this.age = age;
// 模拟复杂的初始化过程
System.out.println("正在创建原型对象,这个过程很耗时...");
try {
Thread.sleep(1000); // 模拟耗时操作
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public Prototype clone() {
try {
return (Prototype) super.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException("克隆失败", e);
}
}
// getter和setter省略...
}
public class PrototypeTest {
public static void main(String[] args) {
System.out.println("=== 传统方式创建对象 ===");
long start1 = System.currentTimeMillis();
Prototype obj1 = new Prototype("张三", 25);
Prototype obj2 = new Prototype("李四", 30);
long end1 = System.currentTimeMillis();
System.out.println("传统方式耗时:" + (end1 - start1) + "ms");
System.out.println("\n=== 原型模式创建对象 ===");
long start2 = System.currentTimeMillis();
Prototype template = new Prototype("模板", 0);
Prototype obj3 = template.clone();
obj3.setName("王五");
obj3.setAge(28);
Prototype obj4 = template.clone();
obj4.setName("赵六");
obj4.setAge(32);
long end2 = System.currentTimeMillis();
System.out.println("原型模式耗时:" + (end2 - start2) + "ms");
}
}
这是原型模式中最重要的概念!很多人都在这里栽过跟头。
/**
* 浅克隆示例 - 对象内部的引用类型不会被克隆
*/
public class ShallowStudent implements Cloneable {
private String name;
private int age;
private List<String> hobbies; // 引用类型
public ShallowStudent(String name, int age) {
this.name = name;
this.age = age;
this.hobbies = new ArrayList<>();
}
@Override
public ShallowStudent clone() {
try {
// 这里只是浅克隆!
return (ShallowStudent) super.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
// getter和setter省略...
}
// 测试浅克隆的问题
public class ShallowCloneTest {
public static void main(String[] args) {
ShallowStudent original = new ShallowStudent("张三", 20);
original.getHobbies().add("篮球");
original.getHobbies().add("游戏");
ShallowStudent clone = original.clone();
clone.setName("李四"); // 基本类型,独立修改
clone.getHobbies().add("足球"); // 引用类型,会影响原对象!
System.out.println("原对象爱好:" + original.getHobbies()); // [篮球, 游戏, 足球]
System.out.println("克隆对象爱好:" + clone.getHobbies()); // [篮球, 游戏, 足球]
// 坑!两个对象共享同一个hobbies列表!
}
}
/**
* 深克隆示例 - 递归克隆所有引用类型
*/
public class DeepStudent implements Cloneable {
private String name;
private int age;
private List<String> hobbies;
private Address address; // 自定义对象
public DeepStudent(String name, int age) {
this.name = name;
this.age = age;
this.hobbies = new ArrayList<>();
this.address = new Address();
}
@Override
public DeepStudent clone() {
try {
DeepStudent cloned = (DeepStudent) super.clone();
// 深克隆引用类型
cloned.hobbies = new ArrayList<>(this.hobbies);
cloned.address = this.address.clone();
return cloned;
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
}
/**
* 地址类也需要支持克隆
*/
public class Address implements Cloneable {
private String city;
private String street;
@Override
public Address clone() {
try {
return (Address) super.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
// getter和setter省略...
}
/**
* 通过序列化实现深克隆 - 最简单的方式
*/
public class SerializableStudent implements Serializable {
private String name;
private int age;
private List<String> hobbies;
private Address address;
// 构造函数省略...
/**
* 通过序列化实现深克隆
*/
public SerializableStudent deepClone() {
try {
// 序列化
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
oos.close();
// 反序列化
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
SerializableStudent cloned = (SerializableStudent) ois.readObject();
ois.close();
return cloned;
} catch (Exception e) {
throw new RuntimeException("深克隆失败", e);
}
}
}
在实际项目中,我们通常会创建一个原型管理器来统一管理原型对象:
/**
* 原型管理器 - 管理和提供各种原型对象
*/
public class PrototypeManager {
private Map<String, Cloneable> prototypes = new HashMap<>();
// 单例模式
private static PrototypeManager instance = new PrototypeManager();
private PrototypeManager() {
// 初始化常用原型
prototypes.put("user", new User("默认用户", 0));
prototypes.put("product", new Product("默认商品", 0.0));
prototypes.put("order", new Order());
}
public static PrototypeManager getInstance() {
return instance;
}
/**
* 获取原型的克隆
*/
public Object getPrototype(String key) {
Cloneable prototype = prototypes.get(key);
if (prototype == null) {
throw new IllegalArgumentException("原型不存在: " + key);
}
try {
Method cloneMethod = prototype.getClass().getMethod("clone");
return cloneMethod.invoke(prototype);
} catch (Exception e) {
throw new RuntimeException("克隆失败", e);
}
}
/**
* 注册新的原型
*/
public void registerPrototype(String key, Cloneable prototype) {
prototypes.put(key, prototype);
}
}
// 使用示例
public class PrototypeManagerTest {
public static void main(String[] args) {
PrototypeManager manager = PrototypeManager.getInstance();
// 快速创建用户对象
User user1 = (User) manager.getPrototype("user");
user1.setName("张三");
User user2 = (User) manager.getPrototype("user");
user2.setName("李四");
}
}
/**
* 抽象原型类 - 定义克隆接口
*/
public abstract class AbstractPrototype implements Cloneable {
@Override
public AbstractPrototype clone() {
try {
return (AbstractPrototype) super.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException("克隆失败", e);
}
}
/**
* 深克隆方法 - 子类需要重写
*/
public abstract AbstractPrototype deepClone();
}
/**
* 具体原型类
*/
public class ConcretePrototype extends AbstractPrototype {
private String data;
private List<String> list;
public ConcretePrototype(String data) {
this.data = data;
this.list = new ArrayList<>();
}
@Override
public ConcretePrototype deepClone() {
ConcretePrototype clone = (ConcretePrototype) this.clone();
clone.list = new ArrayList<>(this.list);
return clone;
}
// getter和setter省略...
}
/**
* 结合建造者模式的原型对象
*/
public class ConfigurablePrototype implements Cloneable {
private String type;
private Map<String, Object> properties;
private ConfigurablePrototype(Builder builder) {
this.type = builder.type;
this.properties = new HashMap<>(builder.properties);
}
@Override
public ConfigurablePrototype clone() {
try {
ConfigurablePrototype cloned = (ConfigurablePrototype) super.clone();
cloned.properties = new HashMap<>(this.properties);
return cloned;
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
public static class Builder {
private String type;
private Map<String, Object> properties = new HashMap<>();
public Builder setType(String type) {
this.type = type;
return this;
}
public Builder addProperty(String key, Object value) {
this.properties.put(key, value);
return this;
}
public ConfigurablePrototype build() {
return new ConfigurablePrototype(this);
}
}
}
// 使用示例
ConfigurablePrototype template = new ConfigurablePrototype.Builder()
.setType("game-character")
.addProperty("level", 1)
.addProperty("health", 100)
.build();
ConfigurablePrototype character1 = template.clone();
ConfigurablePrototype character2 = template.clone();
/**
* 循环引用的问题
*/
public class Node implements Cloneable {
private String name;
private Node parent;
private List<Node> children;
@Override
public Node clone() {
try {
Node cloned = (Node) super.clone();
// 这里会导致无限递归!
cloned.children = new ArrayList<>();
for (Node child : this.children) {
cloned.children.add(child.clone()); // 危险!
}
return cloned;
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
}
解决方案:
public Node clone() {
return clone(new HashMap<>());
}
private Node clone(Map<Node, Node> clonedNodes) {
if (clonedNodes.containsKey(this)) {
return clonedNodes.get(this); // 避免重复克隆
}
try {
Node cloned = (Node) super.clone();
clonedNodes.put(this, cloned);
cloned.children = new ArrayList<>();
for (Node child : this.children) {
cloned.children.add(child.clone(clonedNodes));
}
return cloned;
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
/**
* 性能优化建议
*/
public class OptimizedPrototype implements Cloneable {
private String immutableData; // 不可变对象可以共享
private List<String> mutableData; // 可变对象需要克隆
@Override
public OptimizedPrototype clone() {
try {
OptimizedPrototype cloned = (OptimizedPrototype) super.clone();
// 不可变对象可以共享,节省内存
// cloned.immutableData = this.immutableData; // 这行其实不需要
// 只克隆可变对象
cloned.mutableData = new ArrayList<>(this.mutableData);
return cloned;
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
}
答:
答:
深克隆实现方法:
答:
答:
答:
Spring中Bean的scope可以设置为prototype
:
@Component
@Scope("prototype") // 每次获取都创建新实例
public class PrototypeBean {
// ...
}
// 或者在XML中配置
<bean id="prototypeBean" class="com.example.PrototypeBean" scope="prototype"/>
每次从容器获取都会创建新的实例,类似于原型模式的效果。
原型模式虽然看起来简单,但要用好需要注意很多细节:
原型模式的精髓是"复制"而非"创建"。它解决的是对象创建成本高的问题,而不是对象创建复杂度的问题。
“复制粘贴虽好,但要记住:复制的是结构,粘贴的是新生命!”