原型模式
大约 9 分钟
原型模式
什么是原型模式
原型模式(Prototype Pattern)是一种创建型设计模式,它允许你通过复制现有对象实例来创建新的对象实例,而无需知道其具体类。原型模式的核心思想是通过克隆来创建对象,而不是通过new关键字。
原型模式的核心思想是:
- 通过复制现有对象来创建新对象
- 无需知道对象的具体类
- 提高对象创建的性能和效率
为什么需要原型模式
在实际开发中,有些对象的创建过程可能非常复杂或耗时,比如:
- 对象需要通过复杂初始化过程创建
- 对象需要从数据库或其他外部资源加载数据
- 对象的创建需要大量计算资源
如果每次都通过new关键字创建新对象,会导致性能问题。原型模式通过克隆现有对象来创建新对象,可以大大提高性能。
原型模式的结构
原型模式包含以下几个角色:
- 抽象原型(Prototype):声明克隆自身的接口
- 具体原型(ConcretePrototype):实现克隆自身的操作
- 客户端(Client):让原型对象克隆自身从而创建新对象
原型模式的实现
基本实现
// 抽象原型类
interface Prototype {
Prototype clone();
}
// 具体原型类
class ConcretePrototype implements Prototype {
private String field;
public ConcretePrototype(String field) {
this.field = field;
}
public ConcretePrototype(ConcretePrototype prototype) {
this.field = prototype.field;
}
@Override
public Prototype clone() {
return new ConcretePrototype(this);
}
public String getField() {
return field;
}
public void setField(String field) {
this.field = field;
}
@Override
public String toString() {
return "ConcretePrototype{field='" + field + "'}";
}
}
// 使用示例
public class PrototypeDemo {
public static void main(String[] args) {
// 创建原型对象
ConcretePrototype prototype = new ConcretePrototype("原始值");
System.out.println("原型对象: " + prototype);
// 克隆原型对象
ConcretePrototype clone = (ConcretePrototype) prototype.clone();
System.out.println("克隆对象: " + clone);
// 修改克隆对象的值
clone.setField("修改后的值");
System.out.println("修改后原型对象: " + prototype);
System.out.println("修改后克隆对象: " + clone);
}
}
使用Java内置Cloneable接口实现
// 实现Cloneable接口的原型类
class Person implements Cloneable {
private String name;
private int age;
private Address address;
public Person(String name, int age, Address address) {
this.name = name;
this.age = age;
this.address = address;
}
// 浅克隆
@Override
public Person clone() throws CloneNotSupportedException {
return (Person) super.clone();
}
// 深克隆
public Person deepClone() throws CloneNotSupportedException {
Person cloned = (Person) super.clone();
// 手动克隆引用类型的字段
cloned.address = this.address.clone();
return cloned;
}
// Getter和Setter方法
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
public Address getAddress() { return address; }
public void setAddress(Address address) { this.address = address; }
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + ", address=" + address + "}";
}
}
// 地址类
class Address implements Cloneable {
private String street;
private String city;
public Address(String street, String city) {
this.street = street;
this.city = city;
}
@Override
public Address clone() throws CloneNotSupportedException {
return (Address) super.clone();
}
// Getter和Setter方法
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; }
@Override
public String toString() {
return "Address{street='" + street + "', city='" + city + "'}";
}
}
// 使用示例
public class CloneableDemo {
public static void main(String[] args) throws CloneNotSupportedException {
// 创建原型对象
Address address = new Address("中山路123号", "北京");
Person person = new Person("张三", 25, address);
System.out.println("原型对象: " + person);
// 浅克隆
Person shallowClone = person.clone();
System.out.println("浅克隆对象: " + shallowClone);
// 修改原型对象的引用类型字段
address.setStreet("长安街456号");
System.out.println("修改后原型对象: " + person);
System.out.println("修改后浅克隆对象: " + shallowClone);
System.out.println("--- 分隔线 ---");
// 重新创建原型对象
Address address2 = new Address("南京路789号", "上海");
Person person2 = new Person("李四", 30, address2);
System.out.println("原型对象: " + person2);
// 深克隆
Person deepClone = person2.deepClone();
System.out.println("深克隆对象: " + deepClone);
// 修改原型对象的引用类型字段
address2.setStreet("淮海路101号");
System.out.println("修改后原型对象: " + person2);
System.out.println("修改后深克隆对象: " + deepClone);
}
}
实际应用示例
// 文档模板类
class Document implements Cloneable {
private String title;
private String content;
private List<String> attachments;
private String author;
private String createTime;
public Document(String title, String content) {
this.title = title;
this.content = content;
this.attachments = new ArrayList<>();
this.author = "系统";
this.createTime = new java.util.Date().toString();
}
// 私有构造函数,用于克隆
private Document(Document document) {
this.title = document.title;
this.content = document.content;
// 深克隆列表
this.attachments = new ArrayList<>(document.attachments);
this.author = document.author;
this.createTime = new java.util.Date().toString(); // 新的创建时间
}
@Override
public Document clone() {
return new Document(this);
}
// 添加附件
public void addAttachment(String attachment) {
this.attachments.add(attachment);
}
// Getter和Setter方法
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
public String getContent() { return content; }
public void setContent(String content) { this.content = content; }
public List<String> getAttachments() { return attachments; }
public String getAuthor() { return author; }
public void setAuthor(String author) { this.author = author; }
public String getCreateTime() { return createTime; }
@Override
public String toString() {
return "Document{" +
"title='" + title + '\'' +
", content='" + content + '\'' +
", attachments=" + attachments +
", author='" + author + '\'' +
", createTime='" + createTime + '\'' +
'}';
}
}
// 文档管理器
class DocumentManager {
private Map<String, Document> templates = new HashMap<>();
// 注册模板
public void registerTemplate(String name, Document document) {
templates.put(name, document);
}
// 基于模板创建新文档
public Document createDocument(String templateName) {
Document template = templates.get(templateName);
if (template != null) {
return template.clone();
}
return null;
}
}
// 使用示例
public class DocumentPrototypeDemo {
public static void main(String[] args) {
DocumentManager manager = new DocumentManager();
// 创建模板
Document contractTemplate = new Document("合同模板", "这里是合同内容...");
contractTemplate.addAttachment("合同附件1.pdf");
contractTemplate.addAttachment("合同附件2.docx");
contractTemplate.setAuthor("法务部");
Document reportTemplate = new Document("报告模板", "这里是报告内容...");
reportTemplate.addAttachment("数据表格.xlsx");
reportTemplate.setAuthor("市场部");
// 注册模板
manager.registerTemplate("contract", contractTemplate);
manager.registerTemplate("report", reportTemplate);
// 基于模板创建新文档
Document newContract = manager.createDocument("contract");
if (newContract != null) {
newContract.setTitle("张三与公司的劳动合同");
newContract.setAuthor("人力资源部");
newContract.setContent("具体的劳动合同内容...");
System.out.println("新合同文档:");
System.out.println(newContract);
}
System.out.println();
Document newReport = manager.createDocument("report");
if (newReport != null) {
newReport.setTitle("2023年Q4销售报告");
newReport.setAuthor("销售部");
newReport.setContent("具体的销售报告内容...");
System.out.println("新报告文档:");
System.out.println(newReport);
}
}
}
原型模式的应用场景
- 对象创建成本高:当创建对象的过程比较复杂或耗时,可以通过克隆来提高性能
- 动态创建对象:当需要动态创建对象,而具体类型在运行时才能确定时
- 避免子类化:当需要避免复杂的工厂类层次结构时
- 文档模板:在文档编辑器中,可以基于模板创建新文档
- 游戏开发:在游戏开发中,可以克隆怪物、道具等游戏对象
原型模式的优缺点
优点
- 性能提升:通过克隆创建对象通常比通过new关键字创建对象更快
- 简化对象创建:隐藏了对象创建的复杂细节
- 动态配置:可以在运行时动态增加或减少产品类
- 减少子类化:避免了复杂的工厂类层次结构
缺点
- 克隆方法实现复杂:需要为每个类实现克隆方法,特别是深克隆
- 违背了开闭原则:当需要增加新的原型类时,必须修改客户端代码
- 深克隆和浅克隆问题:需要正确处理引用类型字段的克隆
原型模式与其他创建型模式的比较
与工厂方法模式的区别
- 工厂方法模式:通过工厂类创建对象,需要知道具体的产品类
- 原型模式:通过克隆现有对象创建新对象,无需知道具体的产品类
与抽象工厂模式的区别
- 抽象工厂模式:关注产品族的创建
- 原型模式:关注单个对象的克隆
原型模式的变体
1. 注册表原型模式
// 原型注册表
class PrototypeRegistry {
private Map<String, Prototype> prototypes = new HashMap<>();
public void registerPrototype(String key, Prototype prototype) {
prototypes.put(key, prototype);
}
public Prototype createPrototype(String key) {
Prototype prototype = prototypes.get(key);
if (prototype != null) {
return prototype.clone();
}
return null;
}
}
// 使用示例
public class RegistryDemo {
public static void main(String[] args) {
PrototypeRegistry registry = new PrototypeRegistry();
// 注册原型
registry.registerPrototype("typeA", new ConcretePrototype("类型A"));
registry.registerPrototype("typeB", new ConcretePrototype("类型B"));
// 创建原型
Prototype cloneA = registry.createPrototype("typeA");
Prototype cloneB = registry.createPrototype("typeB");
System.out.println("克隆A: " + cloneA);
System.out.println("克隆B: " + cloneB);
}
}
2. 序列化实现深克隆
import java.io.*;
// 通过序列化实现深克隆
class SerializablePrototype implements Serializable {
private String name;
private List<String> data;
public SerializablePrototype(String name) {
this.name = name;
this.data = new ArrayList<>();
}
// 通过序列化实现深克隆
public SerializablePrototype deepClone() {
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (SerializablePrototype) ois.readObject();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
// Getter和Setter方法
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public List<String> getData() { return data; }
public void setData(List<String> data) { this.data = data; }
@Override
public String toString() {
return "SerializablePrototype{name='" + name + "', data=" + data + "}";
}
}
实际项目中的应用
// 配置对象原型示例
class Configuration implements Cloneable {
private String appName;
private int maxConnections;
private String databaseUrl;
private List<String> allowedIPs;
private Map<String, String> properties;
public Configuration(String appName) {
this.appName = appName;
this.maxConnections = 100;
this.allowedIPs = new ArrayList<>();
this.properties = new HashMap<>();
}
// 私有构造函数用于克隆
private Configuration(Configuration config) {
this.appName = config.appName;
this.maxConnections = config.maxConnections;
this.databaseUrl = config.databaseUrl;
// 深克隆列表和Map
this.allowedIPs = new ArrayList<>(config.allowedIPs);
this.properties = new HashMap<>(config.properties);
}
@Override
public Configuration clone() {
return new Configuration(this);
}
// 配置方法
public Configuration setMaxConnections(int maxConnections) {
this.maxConnections = maxConnections;
return this;
}
public Configuration setDatabaseUrl(String databaseUrl) {
this.databaseUrl = databaseUrl;
return this;
}
public Configuration addAllowedIP(String ip) {
this.allowedIPs.add(ip);
return this;
}
public Configuration setProperty(String key, String value) {
this.properties.put(key, value);
return this;
}
// Getter方法
public String getAppName() { return appName; }
public int getMaxConnections() { return maxConnections; }
public String getDatabaseUrl() { return databaseUrl; }
public List<String> getAllowedIPs() { return allowedIPs; }
public Map<String, String> getProperties() { return properties; }
@Override
public String toString() {
return "Configuration{" +
"appName='" + appName + '\'' +
", maxConnections=" + maxConnections +
", databaseUrl='" + databaseUrl + '\'' +
", allowedIPs=" + allowedIPs +
", properties=" + properties +
'}';
}
}
// 配置管理器
class ConfigurationManager {
private Configuration defaultConfig;
public ConfigurationManager() {
// 创建默认配置
defaultConfig = new Configuration("默认应用")
.setMaxConnections(50)
.setDatabaseUrl("jdbc:mysql://localhost:3306/default_db")
.addAllowedIP("127.0.0.1")
.addAllowedIP("192.168.1.100")
.setProperty("debug", "false")
.setProperty("logLevel", "INFO");
}
// 基于默认配置创建新配置
public Configuration createConfiguration(String appName) {
Configuration config = defaultConfig.clone();
config.setAppName(appName);
return config;
}
// 创建开发环境配置
public Configuration createDevConfiguration(String appName) {
Configuration config = defaultConfig.clone();
config.setAppName(appName + "-dev")
.setMaxConnections(10)
.setDatabaseUrl("jdbc:mysql://localhost:3306/" + appName + "_dev")
.setProperty("debug", "true")
.setProperty("logLevel", "DEBUG");
return config;
}
// 创建生产环境配置
public Configuration createProdConfiguration(String appName) {
Configuration config = defaultConfig.clone();
config.setAppName(appName + "-prod")
.setMaxConnections(200)
.setDatabaseUrl("jdbc:mysql://prod-server:3306/" + appName)
.addAllowedIP("203.0.113.0") // 生产环境IP
.setProperty("debug", "false")
.setProperty("logLevel", "WARN");
return config;
}
}
// 使用示例
public class ConfigurationDemo {
public static void main(String[] args) {
ConfigurationManager manager = new ConfigurationManager();
// 创建用户服务配置
Configuration userServiceConfig = manager.createConfiguration("user-service");
System.out.println("用户服务配置:");
System.out.println(userServiceConfig);
System.out.println();
// 创建订单服务开发配置
Configuration orderServiceDevConfig = manager.createDevConfiguration("order-service");
System.out.println("订单服务开发配置:");
System.out.println(orderServiceDevConfig);
System.out.println();
// 创建支付服务生产配置
Configuration paymentServiceProdConfig = manager.createProdConfiguration("payment-service");
System.out.println("支付服务生产配置:");
System.out.println(paymentServiceProdConfig);
}
}
总结
原型模式是一种通过克隆现有对象来创建新对象的创建型设计模式。它特别适用于对象创建成本高或需要动态创建对象的场景。
使用原型模式的关键点:
- 识别出需要频繁创建且创建成本高的对象
- 实现克隆方法,注意处理浅克隆和深克隆的问题
- 通过原型注册表管理原型对象,方便复用
- 客户端通过克隆原型对象来创建新对象,而无需知道具体的产品类
原型模式的优点是可以提高对象创建的性能,简化对象创建过程,但也需要注意正确实现克隆方法,特别是深克隆的实现。在现代Java开发中,原型模式常用于配置管理、文档模板、游戏对象克隆等场景。