原型模式 (Prototype Pattern)
什么是原型模式?
原型模式是一种创建型设计模式,它允许我们通过复制现有对象来创建新对象,而不是通过类来实例化。简单来说,就是"克隆"一个已经存在的对象来创建新对象。
为什么使用原型模式?
想象一下这样的场景:
- 你有一个复杂的对象,创建它需要很多步骤和资源
- 你需要创建多个相同或相似的对象
- 你不想重复执行复杂的创建过程
原型模式就像"复制粘贴"一样,让你可以快速得到对象的副本。
核心概念
- 原型接口/抽象类:定义克隆方法的接口
- 具体原型类:实现克隆方法的具体类
- 客户端:使用原型来创建新对象
示例一:文档模板系统
假设你有一个文档管理系统,需要创建不同类型的文档模板。
1. 原型接口
public interface Document extends Cloneable {
Document clone();
void setContent(String content);
void display();
}
2. 具体原型类
public class WordDocument implements Document {
private String title;
private String content;
private String author;
private Date createTime;
public WordDocument(String title, String author) {
this.title = title;
this.author = author;
this.createTime = new Date();
this.content = "";
}
@Override
public Document clone() {
try {
WordDocument clone = (WordDocument) super.clone();
// 深拷贝:创建新的Date对象
clone.createTime = new Date();
return clone;
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
@Override
public void setContent(String content) {
this.content = content;
}
@Override
public void display() {
System.out.println("=== Word文档 ===");
System.out.println("标题: " + title);
System.out.println("作者: " + author);
System.out.println("创建时间: " + createTime);
System.out.println("内容: " + content);
System.out.println("================\n");
}
// getter和setter方法
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
public String getAuthor() { return author; }
public void setAuthor(String author) { this.author = author; }
}
3. 客户端使用
public class DocumentManager {
public static void main(String[] args) {
// 创建一个原始文档模板
WordDocument originalDoc = new WordDocument("项目报告模板", "张三");
originalDoc.setContent("这是一个项目报告的模板内容...");
System.out.println("原始文档:");
originalDoc.display();
// 通过克隆创建新文档
WordDocument newDoc1 = (WordDocument) originalDoc.clone();
newDoc1.setTitle("项目A报告");
newDoc1.setContent("项目A的具体内容...");
WordDocument newDoc2 = (WordDocument) originalDoc.clone();
newDoc2.setTitle("项目B报告");
newDoc2.setContent("项目B的具体内容...");
System.out.println("克隆的文档1:");
newDoc1.display();
System.out.println("克隆的文档2:");
newDoc2.display();
}
}
示例二:游戏角色系统
在游戏中,我们经常需要创建相同类型的角色,比如克隆士兵、怪物等。
1. 游戏角色原型
public abstract class GameCharacter implements Cloneable {
protected String name;
protected int level;
protected int health;
protected int attack;
protected int defense;
protected List<String> skills;
public GameCharacter(String name, int level, int health, int attack, int defense) {
this.name = name;
this.level = level;
this.health = health;
this.attack = attack;
this.defense = defense;
this.skills = new ArrayList<>();
}
public abstract GameCharacter clone();
public abstract void display();
// 添加技能
public void addSkill(String skill) {
skills.add(skill);
}
// 升级
public void levelUp() {
level++;
health += 10;
attack += 5;
defense += 3;
}
}
2. 具体角色类
public class Warrior extends GameCharacter {
private String weapon;
public Warrior(String name, int level, int health, int attack, int defense, String weapon) {
super(name, level, health, attack, defense);
this.weapon = weapon;
addSkill("基础剑术");
}
@Override
public GameCharacter clone() {
try {
Warrior clone = (Warrior) super.clone();
// 深拷贝技能列表
clone.skills = new ArrayList<>(this.skills);
return clone;
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
@Override
public void display() {
System.out.println("=== 战士角色 ===");
System.out.println("姓名: " + name);
System.out.println("等级: " + level);
System.out.println("生命值: " + health);
System.out.println("攻击力: " + attack);
System.out.println("防御力: " + defense);
System.out.println("武器: " + weapon);
System.out.println("技能: " + skills);
System.out.println("================\n");
}
public void setWeapon(String weapon) {
this.weapon = weapon;
}
}
public class Mage extends GameCharacter {
private String element;
public Mage(String name, int level, int health, int attack, int defense, String element) {
super(name, level, health, attack, defense);
this.element = element;
addSkill("基础魔法");
}
@Override
public GameCharacter clone() {
try {
Mage clone = (Mage) super.clone();
// 深拷贝技能列表
clone.skills = new ArrayList<>(this.skills);
return clone;
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
@Override
public void display() {
System.out.println("=== 法师角色 ===");
System.out.println("姓名: " + name);
System.out.println("等级: " + level);
System.out.println("生命值: " + health);
System.out.println("攻击力: " + attack);
System.out.println("防御力: " + defense);
System.out.println("元素: " + element);
System.out.println("技能: " + skills);
System.out.println("================\n");
}
public void setElement(String element) {
this.element = element;
}
}
3. 角色管理器
public class CharacterManager {
private Map<String, GameCharacter> characterPrototypes = new HashMap<>();
// 注册原型
public void registerPrototype(String key, GameCharacter character) {
characterPrototypes.put(key, character);
}
// 通过原型创建新角色
public GameCharacter createCharacter(String key, String name) {
GameCharacter prototype = characterPrototypes.get(key);
if (prototype != null) {
GameCharacter clone = prototype.clone();
// 设置新的名字
try {
Field nameField = GameCharacter.class.getDeclaredField("name");
nameField.setAccessible(true);
nameField.set(clone, name);
} catch (Exception e) {
e.printStackTrace();
}
return clone;
}
return null;
}
public static void main(String[] args) {
CharacterManager manager = new CharacterManager();
// 创建原型角色
Warrior warriorPrototype = new Warrior("战士模板", 1, 100, 15, 10, "铁剑");
warriorPrototype.addSkill("冲锋");
warriorPrototype.addSkill("格挡");
Mage magePrototype = new Mage("法师模板", 1, 80, 20, 5, "火");
magePrototype.addSkill("火球术");
magePrototype.addSkill("护盾术");
// 注册原型
manager.registerPrototype("warrior", warriorPrototype);
manager.registerPrototype("mage", magePrototype);
System.out.println("原始原型:");
warriorPrototype.display();
magePrototype.display();
// 通过原型创建新角色
Warrior warrior1 = (Warrior) manager.createCharacter("warrior", "亚瑟");
Warrior warrior2 = (Warrior) manager.createCharacter("warrior", "兰斯洛特");
Mage mage1 = (Mage) manager.createCharacter("mage", "梅林");
// 为新角色设置不同的属性
warrior1.setWeapon("圣剑");
warrior1.levelUp();
warrior2.setWeapon("长枪");
mage1.setElement("冰");
mage1.addSkill("冰锥术");
System.out.println("克隆的角色:");
warrior1.display();
warrior2.display();
mage1.display();
}
}
示例三:配置对象克隆
在实际开发中,我们经常需要复制配置对象。
public class DatabaseConfig implements Cloneable {
private String host;
private int port;
private String username;
private String password;
private String database;
private Map<String, String> properties;
public DatabaseConfig(String host, int port, String username, String password, String database) {
this.host = host;
this.port = port;
this.username = username;
this.password = password;
this.database = database;
this.properties = new HashMap<>();
}
@Override
public DatabaseConfig clone() {
try {
DatabaseConfig clone = (DatabaseConfig) super.clone();
// 深拷贝properties
clone.properties = new HashMap<>(this.properties);
return clone;
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
public void addProperty(String key, String value) {
properties.put(key, value);
}
public void display() {
System.out.println("=== 数据库配置 ===");
System.out.println("主机: " + host);
System.out.println("端口: " + port);
System.out.println("用户名: " + username);
System.out.println("密码: " + password);
System.out.println("数据库: " + database);
System.out.println("属性: " + properties);
System.out.println("==================\n");
}
// getter和setter方法
public String getHost() { return host; }
public void setHost(String host) { this.host = host; }
public int getPort() { return port; }
public void setPort(int port) { this.port = port; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
public String getDatabase() { return database; }
public void setDatabase(String database) { this.database = database; }
}
// 使用示例
public class ConfigManager {
public static void main(String[] args) {
// 创建主数据库配置
DatabaseConfig mainConfig = new DatabaseConfig("localhost", 3306, "root", "password", "main_db");
mainConfig.addProperty("charset", "utf8");
mainConfig.addProperty("timeout", "30");
System.out.println("主数据库配置:");
mainConfig.display();
// 克隆配置用于测试环境
DatabaseConfig testConfig = mainConfig.clone();
testConfig.setHost("test-server");
testConfig.setDatabase("test_db");
testConfig.setPassword("test_password");
testConfig.addProperty("debug", "true");
// 克隆配置用于生产环境
DatabaseConfig prodConfig = mainConfig.clone();
prodConfig.setHost("prod-server");
prodConfig.setDatabase("prod_db");
prodConfig.setPassword("prod_password");
prodConfig.addProperty("max_connections", "100");
System.out.println("测试环境配置:");
testConfig.display();
System.out.println("生产环境配置:");
prodConfig.display();
}
}
原型模式的优缺点
优点:
- 性能提升:避免重复执行复杂的初始化过程
- 简化创建:客户端不需要知道具体的创建细节
- 动态性:可以在运行时动态添加或删除原型
- 减少子类:避免创建大量的工厂类
缺点:
- 深拷贝复杂性:对于复杂对象,深拷贝可能比较复杂
- 内存开销:如果原型对象很大,克隆会消耗更多内存
- 循环引用:需要注意处理对象间的循环引用问题
使用场景
- 对象创建成本高:当创建对象需要大量资源或复杂计算时
- 需要大量相似对象:如游戏中的 NPC、文档模板等
- 避免工厂类爆炸:当有大量相似的产品类时
- 配置对象复制:如数据库连接配置、系统配置等
总结
原型模式就像"复制粘贴"一样简单,但它能大大提高对象创建的效率。当你需要创建大量相似对象时,原型模式是一个很好的选择。记住要正确处理深拷贝,确保克隆的对象之间不会相互影响。
