设计模式-原型模式详解
原型模式详解
目录
- 原型模式简介
- 核心流程
- 重难点分析
- Spring中的源码分析
- 具体使用场景
- 面试高频点
- 使用总结
原型模式简介
定义
原型模式(Prototype Pattern)是一种创建型设计模式,它通过复制现有实例来创建新的实例,而不是通过新建类的方式。原型模式允许一个对象再创建另外一个可定制的对象,而无需知道任何创建的细节。
核心思想
- 克隆创建:通过克隆现有对象来创建新对象
- 避免重复创建:避免重复创建相似对象的开销
- 动态配置:可以在运行时动态配置对象
- 简化创建过程:简化复杂对象的创建过程
模式结构
- Prototype(抽象原型类):声明克隆自身的接口
- ConcretePrototype(具体原型类):实现克隆自身的操作
- Client(客户端):使用原型实例克隆出新的实例
核心流程
原型模式流程图
基本实现流程
1. 定义抽象原型接口
// 抽象原型接口
public interface Prototype {Prototype clone();
}
2. 实现具体原型类
// 具体原型类
public class ConcretePrototype implements Prototype {private String name;private int value;private List<String> items;public ConcretePrototype(String name, int value, List<String> items) {this.name = name;this.value = value;this.items = new ArrayList<>(items);}// 复制构造函数public ConcretePrototype(ConcretePrototype prototype) {this.name = prototype.name;this.value = prototype.value;this.items = new ArrayList<>(prototype.items);}@Overridepublic Prototype clone() {return new ConcretePrototype(this);}// getter和setter方法public String getName() { return name; }public void setName(String name) { this.name = name; }public int getValue() { return value; }public void setValue(int value) { this.value = value; }public List<String> getItems() { return items; }public void setItems(List<String> items) { this.items = items; }@Overridepublic String toString() {return "ConcretePrototype{" +"name='" + name + '\'' +", value=" + value +", items=" + items +'}';}
}
3. 客户端使用
public class Client {public static void main(String[] args) {// 创建原型对象List<String> items = Arrays.asList("item1", "item2", "item3");ConcretePrototype prototype = new ConcretePrototype("原型", 100, items);// 克隆对象ConcretePrototype clone1 = (ConcretePrototype) prototype.clone();ConcretePrototype clone2 = (ConcretePrototype) prototype.clone();// 修改克隆对象clone1.setName("克隆1");clone1.setValue(200);clone1.getItems().add("item4");clone2.setName("克隆2");clone2.setValue(300);clone2.getItems().add("item5");// 输出结果System.out.println("原型: " + prototype);System.out.println("克隆1: " + clone1);System.out.println("克隆2: " + clone2);}
}
重难点分析
重难点1:浅克隆与深克隆
问题描述
如何正确实现浅克隆和深克隆,避免克隆过程中的问题。
解决方案
// 浅克隆实现
public class ShallowClone implements Cloneable {private String name;private int value;private List<String> items;public ShallowClone(String name, int value, List<String> items) {this.name = name;this.value = value;this.items = items;}@Overridepublic Object clone() throws CloneNotSupportedException {return super.clone(); // 浅克隆}// getter和setter方法
}// 深克隆实现
public class DeepClone implements Cloneable {private String name;private int value;private List<String> items;public DeepClone(String name, int value, List<String> items) {this.name = name;this.value = value;this.items = new ArrayList<>(items);}@Overridepublic Object clone() throws CloneNotSupportedException {DeepClone cloned = (DeepClone) super.clone();// 深克隆:复制引用对象cloned.items = new ArrayList<>(this.items);return cloned;}// getter和setter方法
}// 使用序列化实现深克隆
public class SerializableClone implements Serializable {private String name;private int value;private List<String> items;public SerializableClone(String name, int value, List<String> items) {this.name = name;this.value = value;this.items = new ArrayList<>(items);}public SerializableClone deepClone() {try {ByteArrayOutputStream baos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(baos);oos.writeObject(this);ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());ObjectInputStream ois = new ObjectInputStream(bais);return (SerializableClone) ois.readObject();} catch (Exception e) {throw new RuntimeException("深克隆失败", e);}}// getter和setter方法
}
重难点2:原型注册表管理
问题描述
如何管理多个原型对象,提供统一的克隆接口。
解决方案
// 原型注册表
public class PrototypeRegistry {private static final Map<String, Prototype> prototypes = new HashMap<>();static {// 注册默认原型prototypes.put("default", new ConcretePrototype("默认", 0, new ArrayList<>()));prototypes.put("template1", new ConcretePrototype("模板1", 100, Arrays.asList("item1", "item2")));prototypes.put("template2", new ConcretePrototype("模板2", 200, Arrays.asList("item3", "item4")));}public static void registerPrototype(String name, Prototype prototype) {prototypes.put(name, prototype);}public static Prototype getPrototype(String name) {Prototype prototype = prototypes.get(name);if (prototype == null) {throw new IllegalArgumentException("原型不存在: " + name);}return prototype.clone();}public static List<String> getPrototypeNames() {return new ArrayList<>(prototypes.keySet());}
}// 使用示例
public class PrototypeRegistryDemo {public static void main(String[] args) {// 注册自定义原型PrototypeRegistry.registerPrototype("custom", new ConcretePrototype("自定义", 300, Arrays.asList("item5", "item6")));// 获取原型ConcretePrototype prototype1 = (ConcretePrototype) PrototypeRegistry.getPrototype("template1");ConcretePrototype prototype2 = (ConcretePrototype) PrototypeRegistry.getPrototype("custom");System.out.println("原型1: " + prototype1);System.out.println("原型2: " + prototype2);}
}
重难点3:原型模式的线程安全
问题描述
在多线程环境下,如何确保原型模式的线程安全。
解决方案
// 线程安全的原型模式
public class ThreadSafePrototype implements Cloneable {private final String name;private final int value;private final List<String> items;private final Object lock = new Object();public ThreadSafePrototype(String name, int value, List<String> items) {this.name = name;this.value = value;this.items = new ArrayList<>(items);}@Overridepublic synchronized Object clone() throws CloneNotSupportedException {ThreadSafePrototype cloned = (ThreadSafePrototype) super.clone();// 深克隆cloned.items = new ArrayList<>(this.items);return cloned;}public ThreadSafePrototype cloneSafely() {synchronized (lock) {try {return (ThreadSafePrototype) clone();} catch (CloneNotSupportedException e) {throw new RuntimeException("克隆失败", e);}}}// getter方法public String getName() { return name; }public int getValue() { return value; }public List<String> getItems() { return new ArrayList<>(items); }
}// 使用ConcurrentHashMap的原型注册表
public class ConcurrentPrototypeRegistry {private static final ConcurrentHashMap<String, Prototype> prototypes = new ConcurrentHashMap<>();public static void registerPrototype(String name, Prototype prototype) {prototypes.put(name, prototype);}public static Prototype getPrototype(String name) {Prototype prototype = prototypes.get(name);if (prototype == null) {throw new IllegalArgumentException("原型不存在: " + name);}return prototype.clone();}
}
重难点4:原型模式的性能优化
问题描述
如何优化原型模式的性能,避免频繁的对象创建。
解决方案
// 对象池模式结合原型模式
public class PrototypePool {private final Queue<Prototype> pool = new ConcurrentLinkedQueue<>();private final Prototype prototype;private final int maxSize;public PrototypePool(Prototype prototype, int maxSize) {this.prototype = prototype;this.maxSize = maxSize;}public Prototype acquire() {Prototype obj = pool.poll();if (obj == null) {obj = prototype.clone();}return obj;}public void release(Prototype obj) {if (pool.size() < maxSize) {// 重置对象状态resetObject(obj);pool.offer(obj);}}private void resetObject(Prototype obj) {// 重置对象状态的具体实现if (obj instanceof Resettable) {((Resettable) obj).reset();}}
}// 可重置接口
public interface Resettable {void reset();
}// 缓存原型对象
public class CachedPrototype implements Prototype {private static final Map<String, Prototype> cache = new ConcurrentHashMap<>();private final String key;private final Prototype prototype;public CachedPrototype(String key, Prototype prototype) {this.key = key;this.prototype = prototype;}@Overridepublic Prototype clone() {return cache.computeIfAbsent(key, k -> prototype.clone());}
}
Spring中的源码分析
Spring Bean的原型模式
// AbstractBeanFactory中的原型Bean处理
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {@Overridepublic Object getBean(String name) throws BeansException {return doGetBean(name, null, null, false);}@Overridepublic <T> T getBean(String name, Class<T> requiredType) throws BeansException {return doGetBean(name, requiredType, null, false);}@SuppressWarnings("unchecked")protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException {String beanName = transformedBeanName(name);Object bean;// 尝试从缓存中获取单例BeanObject sharedInstance = getSingleton(beanName);if (sharedInstance != null && args == null) {bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);} else {// 检查Bean定义是否存在BeanDefinition mbd = getMergedBeanDefinition(beanName);if (mbd.isSingleton()) {sharedInstance = getSingleton(beanName, () -> {try {return createBean(beanName, mbd, args);} catch (BeansException ex) {destroySingleton(beanName);throw ex;}});bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);} else if (mbd.isPrototype()) {// 原型Bean:每次创建新实例Object prototypeInstance = null;try {beforePrototypeCreation(beanName);prototypeInstance = createBean(beanName, mbd, args);} finally {afterPrototypeCreation(beanName);}bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);} else {// 其他作用域的BeanString scopeName = mbd.getScope();Scope scope = this.scopes.get(scopeName);if (scope == null) {throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");}try {Object scopedInstance = scope.get(beanName, () -> {beforePrototypeCreation(beanName);try {return createBean(beanName, mbd, args);} finally {afterPrototypeCreation(beanName);}});bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);} catch (IllegalStateException ex) {throw new BeanCreationException(beanName, "Scope '" + scopeName + "' is not active for the current thread", ex);}}}return adaptBeanInstance(name, bean, requiredType);}
}
Spring的ObjectUtils.cloneIfPossible
// ObjectUtils中的克隆方法
public class ObjectUtils {@SuppressWarnings("unchecked")public static <T> T cloneIfPossible(T obj) {if (obj instanceof Cloneable) {try {return (T) obj.getClass().getMethod("clone").invoke(obj);} catch (Exception ex) {// 克隆失败,返回原对象return obj;}}return obj;}public static <T> T clone(T obj) {if (obj == null) {return null;}if (obj instanceof Cloneable) {try {return (T) obj.getClass().getMethod("clone").invoke(obj);} catch (Exception ex) {throw new RuntimeException("克隆失败", ex);}}throw new IllegalArgumentException("对象不支持克隆: " + obj.getClass());}
}
Spring的BeanWrapper
// BeanWrapper中的属性复制
public class BeanWrapperImpl extends PropertyAccessorFactory implements BeanWrapper {@Overridepublic void copyProperties(Object source, Object target) {copyProperties(source, target, null, (String[]) null);}@Overridepublic void copyProperties(Object source, Object target, String... ignoreProperties) {copyProperties(source, target, null, ignoreProperties);}private void copyProperties(Object source, Object target, @Nullable Class<?> editable, @Nullable String... ignoreProperties) {Assert.notNull(source, "Source must not be null");Assert.notNull(target, "Target must not be null");Class<?> actualEditable = target.getClass();if (editable != null) {if (!editable.isInstance(target)) {throw new IllegalArgumentException("Target class [" + target.getClass().getName() + "] not assignable to Editable class [" + editable.getName() + "]");}actualEditable = editable;}PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);List<String> ignoreList = (ignoreProperties != null ? Arrays.asList(ignoreProperties) : Collections.emptyList());for (PropertyDescriptor targetPd : targetPds) {Method writeMethod = targetPd.getWriteMethod();if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());if (sourcePd != null) {Method readMethod = sourcePd.getReadMethod();if (readMethod != null && ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {try {if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {readMethod.setAccessible(true);}Object value = readMethod.invoke(source);if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {writeMethod.setAccessible(true);}writeMethod.invoke(target, value);} catch (Throwable ex) {throw new FatalBeanException("Could not copy property '" + targetPd.getName() + "' from source to target", ex);}}}}}}
}
具体使用场景
1. 配置对象克隆
// 配置对象
public class DatabaseConfig implements Cloneable {private String host;private int port;private String database;private String username;private String password;private Map<String, String> properties;public DatabaseConfig(String host, int port, String database, String username, String password) {this.host = host;this.port = port;this.database = database;this.username = username;this.password = password;this.properties = new HashMap<>();}@Overridepublic DatabaseConfig clone() {try {DatabaseConfig cloned = (DatabaseConfig) super.clone();// 深克隆cloned.properties = new HashMap<>(this.properties);return cloned;} catch (CloneNotSupportedException e) {throw new RuntimeException("克隆失败", e);}}// 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 getDatabase() { return database; }public void setDatabase(String database) { this.database = database; }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 Map<String, String> getProperties() { return properties; }public void setProperties(Map<String, String> properties) { this.properties = properties; }
}// 配置管理器
public class ConfigManager {private static final Map<String, DatabaseConfig> configs = new HashMap<>();static {// 默认配置DatabaseConfig defaultConfig = new DatabaseConfig("localhost", 3306, "test", "root", "password");configs.put("default", defaultConfig);// 开发环境配置DatabaseConfig devConfig = defaultConfig.clone();devConfig.setDatabase("dev_db");configs.put("dev", devConfig);// 生产环境配置DatabaseConfig prodConfig = defaultConfig.clone();prodConfig.setHost("prod-server");prodConfig.setDatabase("prod_db");prodConfig.setUsername("prod_user");prodConfig.setPassword("prod_password");configs.put("prod", prodConfig);}public static DatabaseConfig getConfig(String environment) {DatabaseConfig config = configs.get(environment);if (config == null) {throw new IllegalArgumentException("配置不存在: " + environment);}return config.clone();}
}
2. 文档模板克隆
// 文档模板
public class DocumentTemplate implements Cloneable {private String title;private String content;private List<String> sections;private Map<String, String> metadata;public DocumentTemplate(String title, String content) {this.title = title;this.content = content;this.sections = new ArrayList<>();this.metadata = new HashMap<>();}@Overridepublic DocumentTemplate clone() {try {DocumentTemplate cloned = (DocumentTemplate) super.clone();// 深克隆cloned.sections = new ArrayList<>(this.sections);cloned.metadata = new HashMap<>(this.metadata);return cloned;} catch (CloneNotSupportedException e) {throw new RuntimeException("克隆失败", e);}}public DocumentTemplate createDocument(String title, String content) {DocumentTemplate document = this.clone();document.setTitle(title);document.setContent(content);return document;}// 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> getSections() { return sections; }public void setSections(List<String> sections) { this.sections = sections; }public Map<String, String> getMetadata() { return metadata; }public void setMetadata(Map<String, String> metadata) { this.metadata = metadata; }
}// 文档管理器
public class DocumentManager {private static final Map<String, DocumentTemplate> templates = new HashMap<>();static {// 创建模板DocumentTemplate reportTemplate = new DocumentTemplate("报告模板", "这是一个报告模板");reportTemplate.getSections().add("摘要");reportTemplate.getSections().add("正文");reportTemplate.getSections().add("结论");reportTemplate.getMetadata().put("author", "系统");reportTemplate.getMetadata().put("version", "1.0");templates.put("report", reportTemplate);DocumentTemplate letterTemplate = new DocumentTemplate("信件模板", "这是一个信件模板");letterTemplate.getSections().add("称呼");letterTemplate.getSections().add("正文");letterTemplate.getSections().add("结尾");letterTemplate.getMetadata().put("type", "formal");templates.put("letter", letterTemplate);}public static DocumentTemplate createDocument(String templateName, String title, String content) {DocumentTemplate template = templates.get(templateName);if (template == null) {throw new IllegalArgumentException("模板不存在: " + templateName);}return template.createDocument(title, content);}
}
3. 游戏对象克隆
// 游戏对象
public class GameObject implements Cloneable {private String name;private int health;private int level;private List<String> skills;private Map<String, Object> properties;public GameObject(String name, int health, int level) {this.name = name;this.health = health;this.level = level;this.skills = new ArrayList<>();this.properties = new HashMap<>();}@Overridepublic GameObject clone() {try {GameObject cloned = (GameObject) super.clone();// 深克隆cloned.skills = new ArrayList<>(this.skills);cloned.properties = new HashMap<>(this.properties);return cloned;} catch (CloneNotSupportedException e) {throw new RuntimeException("克隆失败", e);}}public GameObject createInstance(String name) {GameObject instance = this.clone();instance.setName(name);return instance;}// getter和setter方法public String getName() { return name; }public void setName(String name) { this.name = name; }public int getHealth() { return health; }public void setHealth(int health) { this.health = health; }public int getLevel() { return level; }public void setLevel(int level) { this.level = level; }public List<String> getSkills() { return skills; }public void setSkills(List<String> skills) { this.skills = skills; }public Map<String, Object> getProperties() { return properties; }public void setProperties(Map<String, Object> properties) { this.properties = properties; }
}// 游戏对象工厂
public class GameObjectFactory {private static final Map<String, GameObject> prototypes = new HashMap<>();static {// 创建原型GameObject playerPrototype = new GameObject("玩家", 100, 1);playerPrototype.getSkills().add("攻击");playerPrototype.getSkills().add("防御");playerPrototype.getProperties().put("class", "warrior");prototypes.put("player", playerPrototype);GameObject enemyPrototype = new GameObject("敌人", 50, 1);enemyPrototype.getSkills().add("攻击");enemyPrototype.getProperties().put("class", "monster");prototypes.put("enemy", enemyPrototype);}public static GameObject createObject(String type, String name) {GameObject prototype = prototypes.get(type);if (prototype == null) {throw new IllegalArgumentException("对象类型不存在: " + type);}return prototype.createInstance(name);}
}
面试高频点
面试知识点思维导图
1. 原型模式的基本概念
问题:什么是原型模式?
答案要点:
- 通过复制现有实例来创建新的实例
- 属于创建型设计模式
- 避免重复创建相似对象的开销
- 简化复杂对象的创建过程
问题:原型模式有哪些角色?
答案要点:
- Prototype(抽象原型类):声明克隆自身的接口
- ConcretePrototype(具体原型类):实现克隆自身的操作
- Client(客户端):使用原型实例克隆出新的实例
2. 实现方式相关
问题:如何实现原型模式?
答案要点:
// 1. 实现Cloneable接口
public class ConcretePrototype implements Cloneable {private String name;private int value;public ConcretePrototype(String name, int value) {this.name = name;this.value = value;}@Overridepublic ConcretePrototype clone() {try {return (ConcretePrototype) super.clone();} catch (CloneNotSupportedException e) {throw new RuntimeException("克隆失败", e);}}
}// 2. 使用复制构造函数
public class ConcretePrototype {private String name;private int value;public ConcretePrototype(String name, int value) {this.name = name;this.value = value;}// 复制构造函数public ConcretePrototype(ConcretePrototype prototype) {this.name = prototype.name;this.value = prototype.value;}public ConcretePrototype clone() {return new ConcretePrototype(this);}
}
3. 重难点问题
问题:浅克隆和深克隆的区别?
答案要点:
- 浅克隆:只复制基本类型和引用,不复制引用对象
- 深克隆:复制所有属性,包括引用对象
- 实现方式:浅克隆使用super.clone(),深克隆需要手动复制引用对象
- 注意事项:深克隆需要递归复制所有引用对象
问题:如何实现深克隆?
答案要点:
// 1. 手动实现深克隆
@Override
public Object clone() throws CloneNotSupportedException {DeepClone cloned = (DeepClone) super.clone();// 深克隆:复制引用对象cloned.items = new ArrayList<>(this.items);cloned.metadata = new HashMap<>(this.metadata);return cloned;
}// 2. 使用序列化实现深克隆
public DeepClone deepClone() {try {ByteArrayOutputStream baos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(baos);oos.writeObject(this);ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());ObjectInputStream ois = new ObjectInputStream(bais);return (DeepClone) ois.readObject();} catch (Exception e) {throw new RuntimeException("深克隆失败", e);}
}
4. Spring中的应用
问题:Spring中如何使用原型模式?
答案要点:
// 1. 原型Bean
@Component
@Scope("prototype")
public class PrototypeBean {// Bean实现
}// 2. 获取原型Bean
@Autowired
private ApplicationContext applicationContext;public PrototypeBean getPrototypeBean() {return applicationContext.getBean(PrototypeBean.class);
}// 3. 使用ObjectUtils克隆
Object cloned = ObjectUtils.cloneIfPossible(original);
5. 设计原则相关
问题:原型模式体现了哪些设计原则?
答案要点:
- 单一职责:原型只负责克隆操作
- 开闭原则:可以添加新的原型类型
- 依赖倒置:依赖抽象而不是具体实现
- 接口隔离:客户端只依赖需要的接口
6. 实际应用场景
问题:原型模式适用于哪些场景?
答案要点:
- 配置对象:数据库配置、系统配置等
- 文档模板:报告模板、信件模板等
- 游戏对象:角色、道具、场景等
- 缓存对象:需要频繁创建相似对象
- 复杂对象:创建成本较高的复杂对象
使用总结
原型模式的优势
- 性能优化:避免重复创建相似对象的开销
- 简化创建:简化复杂对象的创建过程
- 动态配置:可以在运行时动态配置对象
- 代码复用:通过克隆实现代码复用
原型模式的缺点
- 深克隆复杂:深克隆实现复杂,容易出错
- 内存开销:需要维护原型对象,占用内存
- 循环引用:深克隆时可能遇到循环引用问题
- 性能问题:深克隆可能影响性能
使用建议
- 简单对象:只用于创建简单对象,复杂对象考虑其他模式
- 深克隆:需要深克隆时,考虑使用序列化方式
- 线程安全:多线程环境下注意线程安全
- 性能考虑:权衡克隆成本和创建成本
最佳实践
- 实现Cloneable接口:正确实现Cloneable接口
- 深克隆实现:需要深克隆时,手动复制引用对象
- 异常处理:正确处理CloneNotSupportedException
- 文档注释:为克隆方法添加详细的文档注释
- 单元测试:为克隆功能编写单元测试
与其他模式的对比
- 与工厂模式:原型模式通过克隆创建,工厂模式通过新建创建
- 与建造者模式:原型模式克隆现有对象,建造者模式构建新对象
- 与单例模式:原型模式创建多个实例,单例模式创建唯一实例
原型模式是一种有用的创建型设计模式,特别适用于需要创建大量相似对象、配置对象克隆、模板对象克隆等场景。通过合理使用原型模式,可以大大提高系统的性能和灵活性。