设计模式之二—原型模式:灵活的对象克隆机制

1. 模式概述

1.1 定义

原型模式(Prototype Pattern)是一种创建型设计模式,它通过复制现有对象(称为原型)来创建新对象,而不是通过new关键字实例化。该模式提供了一个原型接口,用于创建当前对象的克隆。

1.2 核心思想

**克隆(Clone)**​ 是原型模式的核心概念。当直接创建对象的成本较高时,通过复制现有对象来创建新对象可以显著提高性能,同时保持对象的初始状态。

2. 适用场景

场景 说明 示例
创建成本高 对象创建需要消耗大量资源 数据库连接、复杂计算对象
构造过程复杂 对象初始化需要多个步骤 配置对象、文档模板
状态相似对象 需要创建多个相似对象 游戏角色、简历模板
避免重复初始化 需要避免重复执行初始化代码 缓存对象、会话对象

3. 实现方式对比

3.1 浅克隆 vs 深克隆

特性 浅克隆 深克隆
基本数据类型 复制值 复制值
引用类型 复制引用 复制引用指向的对象
实现复杂度 简单 复杂
性能 较低
适用场景 无引用或引用不变 包含可变引用对象

3.2 Java实现方法对比

方法 优点 缺点 适用场景
Cloneable.clone() 简单、内置支持 只支持浅克隆 简单对象
手动深克隆 完全控制 代码复杂 复杂对象
序列化 自动深克隆 性能开销 需要序列化的对象
复制构造函数 类型安全 需要额外代码 替代clone()

4. 完整代码实现

4.1 基础原型接口

复制代码
/**
 * 原型接口
 * 声明克隆方法,所有具体原型必须实现
 */
public interface IPrototype extends Cloneable {
    
    /**
     * 克隆方法
     * @return 克隆后的新对象
     * @throws CloneNotSupportedException 当对象不支持克隆时抛出
     */
    IPrototype clone() throws CloneNotSupportedException;
    
    /**
     * 深克隆方法
     * @return 深克隆后的新对象
     * @throws CloneNotSupportedException 当对象不支持克隆时抛出
     */
    IPrototype deepClone() throws CloneNotSupportedException;
    
    /**
     * 显示对象信息
     */
    void display();
}

4.2 具体原型类实现

复制代码
import java.io.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * 简历类 - 具体原型实现
 * 演示浅克隆、深克隆、序列化克隆等多种实现方式
 */
public class Resume implements IPrototype, Serializable {
    
    private static final long serialVersionUID = 1L;
    
    // 基本类型字段
    private String name;
    private int age;
    private String education;
    
    // 引用类型字段
    private Date birthDate;
    private List<String> skills;
    private WorkExperience workExperience;
    
    /**
     * 构造函数
     */
    public Resume(String name, int age, String education) {
        this.name = name;
        this.age = age;
        this.education = education;
        this.birthDate = new Date();
        this.skills = new ArrayList<>();
        this.workExperience = new WorkExperience();
        System.out.println("执行复杂的简历初始化过程...");
        simulateComplexInitialization();
    }
    
    /**
     * 复制构造函数 - 替代clone()的方法
     */
    public Resume(Resume another) {
        this.name = another.name;
        this.age = another.age;
        this.education = another.education;
        this.birthDate = (Date) another.birthDate.clone();
        this.skills = new ArrayList<>(another.skills);
        this.workExperience = new WorkExperience(another.workExperience);
    }
    
    /**
     * 模拟复杂初始化过程
     */
    private void simulateComplexInitialization() {
        try {
            Thread.sleep(1000); // 模拟耗时操作
            loadDefaultSkills();
            initializeWorkExperience();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
    
    private void loadDefaultSkills() {
        skills.add("Java基础");
        skills.add("面向对象编程");
    }
    
    private void initializeWorkExperience() {
        workExperience.setCompany("未设置");
        workExperience.setPosition("未设置");
        workExperience.setYears(0);
    }
    
    //========== 克隆方法实现 ==========
    
    /**
     * 浅克隆实现
     * 只复制基本数据类型和引用
     */
    @Override
    public Resume clone() throws CloneNotSupportedException {
        System.out.println("执行浅克隆...");
        return (Resume) super.clone();
    }
    
    /**
     * 深克隆实现 - 方法1:手动复制
     */
    @Override
    public Resume deepClone() throws CloneNotSupportedException {
        System.out.println("执行深克隆(手动)...");
        Resume cloned = (Resume) super.clone();
        
        // 深度复制所有引用类型字段
        cloned.birthDate = (Date) this.birthDate.clone();
        cloned.skills = new ArrayList<>(this.skills);
        cloned.workExperience = this.workExperience.clone();
        
        return cloned;
    }
    
    /**
     * 深克隆实现 - 方法2:序列化
     */
    public Resume deepCloneBySerialization() throws IOException, ClassNotFoundException {
        System.out.println("执行深克隆(序列化)...");
        
        // 将对象写入字节流
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(this);
        oos.flush();
        
        // 从字节流读取对象
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        
        return (Resume) ois.readObject();
    }
    
    //========== 业务方法 ==========
    
    public void addSkill(String skill) {
        this.skills.add(skill);
    }
    
    public void setWorkExperience(String company, String position, int years) {
        this.workExperience.setCompany(company);
        this.workExperience.setPosition(position);
        this.workExperience.setYears(years);
    }
    
    @Override
    public void display() {
        System.out.println("\n========== 简历详情 ==========");
        System.out.println("姓名: " + name);
        System.out.println("年龄: " + age);
        System.out.println("学历: " + education);
        System.out.println("出生日期: " + birthDate);
        System.out.println("技能列表: " + String.join(", ", skills));
        System.out.println("工作经历: " + workExperience);
        System.out.println("对象哈希码: " + System.identityHashCode(this));
        System.out.println("技能列表哈希码: " + System.identityHashCode(skills));
        System.out.println("==============================\n");
    }
    
    //========== 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 String getEducation() { return education; }
    public void setEducation(String education) { this.education = education; }
    public List<String> getSkills() { return skills; }
    public WorkExperience getWorkExperience() { return workExperience; }
}

/**
 * 工作经验类 - 包含引用类型
 */
class WorkExperience implements Cloneable, Serializable {
    private static final long serialVersionUID = 1L;
    
    private String company;
    private String position;
    private int years;
    private List<String> projects;
    
    public WorkExperience() {
        this.projects = new ArrayList<>();
    }
    
    public WorkExperience(WorkExperience another) {
        this.company = another.company;
        this.position = another.position;
        this.years = another.years;
        this.projects = new ArrayList<>(another.projects);
    }
    
    @Override
    public WorkExperience clone() throws CloneNotSupportedException {
        WorkExperience cloned = (WorkExperience) super.clone();
        cloned.projects = new ArrayList<>(this.projects);
        return cloned;
    }
    
    public void addProject(String project) {
        this.projects.add(project);
    }
    
    @Override
    public String toString() {
        return String.format("%s - %s (%d年) 项目: %s", 
            company, position, years, String.join(", ", projects));
    }
    
    // Getter和Setter
    public String getCompany() { return company; }
    public void setCompany(String company) { this.company = company; }
    public String getPosition() { return position; }
    public void setPosition(String position) { this.position = position; }
    public int getYears() { return years; }
    public void setYears(int years) { this.years = years; }
    public List<String> getProjects() { return projects; }
}

4.3 原型管理器(注册表)

复制代码
import java.util.concurrent.ConcurrentHashMap;
import java.util.Map;

/**
 * 原型管理器(注册表)
 * 管理预定义的原型对象,提供快速克隆
 */
public class PrototypeRegistry {
    
    private static final Map<String, IPrototype> prototypes = new ConcurrentHashMap<>();
    private static volatile PrototypeRegistry instance;
    
    // 私有构造函数,单例模式
    private PrototypeRegistry() {
        initializeDefaultPrototypes();
    }
    
    /**
     * 获取单例实例
     */
    public static PrototypeRegistry getInstance() {
        if (instance == null) {
            synchronized (PrototypeRegistry.class) {
                if (instance == null) {
                    instance = new PrototypeRegistry();
                }
            }
        }
        return instance;
    }
    
    /**
     * 初始化默认原型
     */
    private void initializeDefaultPrototypes() {
        // Java开发人员模板
        Resume javaDeveloper = new Resume("Java开发工程师", 25, "本科");
        javaDeveloper.setWorkExperience("互联网公司", "Java开发", 3);
        javaDeveloper.addSkill("Java");
        javaDeveloper.addSkill("Spring Boot");
        javaDeveloper.addSkill("MySQL");
        javaDeveloper.addSkill("Redis");
        javaDeveloper.getWorkExperience().addProject("电商系统");
        prototypes.put("java-developer", javaDeveloper);
        
        // 前端开发人员模板
        Resume frontendDeveloper = new Resume("前端开发工程师", 24, "本科");
        frontendDeveloper.setWorkExperience("科技公司", "前端开发", 2);
        frontendDeveloper.addSkill("JavaScript");
        frontendDeveloper.addSkill("Vue.js");
        frontendDeveloper.addSkill("React");
        frontendDeveloper.addSkill("TypeScript");
        frontendDeveloper.getWorkExperience().addProject("后台管理系统");
        prototypes.put("frontend-developer", frontendDeveloper);
        
        // 项目经理模板
        Resume projectManager = new Resume("项目经理", 32, "硕士");
        projectManager.setWorkExperience("软件公司", "项目经理", 5);
        projectManager.addSkill("项目管理");
        projectManager.addSkill("团队协作");
        projectManager.addSkill("敏捷开发");
        projectManager.addSkill("需求分析");
        projectManager.getWorkExperience().addProject("企业ERP系统");
        prototypes.put("project-manager", projectManager);
    }
    
    /**
     * 获取原型克隆
     */
    public IPrototype getPrototype(String key) throws CloneNotSupportedException {
        IPrototype prototype = prototypes.get(key);
        if (prototype != null) {
            return prototype.clone();
        }
        throw new IllegalArgumentException("未找到对应的原型: " + key);
    }
    
    /**
     * 获取深克隆原型
     */
    public IPrototype getDeepPrototype(String key) throws CloneNotSupportedException {
        IPrototype prototype = prototypes.get(key);
        if (prototype != null) {
            return prototype.deepClone();
        }
        throw new IllegalArgumentException("未找到对应的原型: " + key);
    }
    
    /**
     * 注册新原型
     */
    public void registerPrototype(String key, IPrototype prototype) {
        prototypes.put(key, prototype);
    }
    
    /**
     * 移除原型
     */
    public void removePrototype(String key) {
        prototypes.remove(key);
    }
    
    /**
     * 获取所有原型键
     */
    public List<String> getAllPrototypeKeys() {
        return new ArrayList<>(prototypes.keySet());
    }
    
    /**
     * 清空所有原型
     */
    public void clearAllPrototypes() {
        prototypes.clear();
    }
}

4.4 客户端使用示例

复制代码
/**
 * 客户端代码 - 演示原型模式的各种使用场景
 */
public class PrototypeClient {
    
    public static void main(String[] args) {
        try {
            System.out.println("========== 原型模式演示 ==========\n");
            
            // 1. 基本克隆演示
            demonstrateBasicClone();
            
            // 2. 浅克隆 vs 深克隆对比
            demonstrateShallowVsDeepClone();
            
            // 3. 原型注册表使用
            demonstratePrototypeRegistry();
            
            // 4. 性能对比测试
            demonstratePerformanceComparison();
            
            // 5. 序列化深克隆
            demonstrateSerializationClone();
            
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    /**
     * 1. 基本克隆演示
     */
    private static void demonstrateBasicClone() throws CloneNotSupportedException {
        System.out.println("1. 基本克隆演示");
        System.out.println("=".repeat(40));
        
        // 创建原始对象
        Resume original = new Resume("张三", 28, "硕士");
        original.setWorkExperience("阿里巴巴", "高级工程师", 5);
        original.addSkill("分布式系统");
        original.addSkill("微服务架构");
        
        System.out.println("原始对象:");
        original.display();
        
        // 使用clone()方法克隆
        Resume cloned = original.clone();
        cloned.setName("李四");
        cloned.setAge(30);
        cloned.addSkill("云计算");
        
        System.out.println("克隆对象(修改后):");
        cloned.display();
        
        System.out.println("原始对象(验证是否受影响):");
        original.display();
    }
    
    /**
     * 2. 浅克隆 vs 深克隆对比
     */
    private static void demonstrateShallowVsDeepClone() 
            throws CloneNotSupportedException {
        System.out.println("\n2. 浅克隆 vs 深克隆对比");
        System.out.println("=".repeat(40));
        
        // 创建包含引用类型的原始对象
        Resume original = new Resume("王五", 26, "本科");
        original.setWorkExperience("腾讯", "开发工程师", 3);
        original.addSkill("Java");
        original.getWorkExperience().addProject("社交应用");
        
        System.out.println("原始对象:");
        original.display();
        
        // 浅克隆
        Resume shallowClone = original.clone();
        shallowClone.setName("浅克隆-王五");
        shallowClone.getSkills().add("Python");  // 修改引用对象
        shallowClone.getWorkExperience().addProject("即时通讯");
        
        System.out.println("浅克隆对象(修改了引用对象):");
        shallowClone.display();
        
        System.out.println("原始对象(被浅克隆影响):");
        original.display();
        
        // 深克隆
        Resume deepClone = original.deepClone();
        deepClone.setName("深克隆-王五");
        deepClone.getSkills().add("Go");
        deepClone.getWorkExperience().addProject("游戏开发");
        
        System.out.println("深克隆对象(修改了引用对象):");
        deepClone.display();
        
        System.out.println("原始对象(不受深克隆影响):");
        original.display();
    }
    
    /**
     * 3. 原型注册表使用
     */
    private static void demonstratePrototypeRegistry() 
            throws CloneNotSupportedException {
        System.out.println("\n3. 原型注册表使用");
        System.out.println("=".repeat(40));
        
        PrototypeRegistry registry = PrototypeRegistry.getInstance();
        
        System.out.println("可用原型模板: " + registry.getAllPrototypeKeys());
        
        // 使用Java开发人员模板
        Resume javaDev = (Resume) registry.getPrototype("java-developer");
        javaDev.setName("赵六");
        javaDev.setAge(27);
        javaDev.addSkill("Docker");
        
        System.out.println("\n基于Java开发模板创建的简历:");
        javaDev.display();
        
        // 使用项目经理模板
        Resume projectManager = (Resume) registry.getDeepPrototype("project-manager");
        projectManager.setName("钱七");
        projectManager.setAge(35);
        projectManager.addSkill("成本控制");
        
        System.out.println("\n基于项目经理模板创建的简历:");
        projectManager.display();
        
        // 注册新模板
        Resume newTemplate = new Resume("全栈工程师", 28, "本科");
        newTemplate.setWorkExperience("创业公司", "全栈开发", 4);
        newTemplate.addSkill("Java");
        newTemplate.addSkill("Vue.js");
        newTemplate.addSkill("Linux");
        registry.registerPrototype("fullstack", newTemplate);
        
        System.out.println("新增原型后可用模板: " + registry.getAllPrototypeKeys());
    }
    
    /**
     * 4. 性能对比测试
     */
    private static void demonstratePerformanceComparison() 
            throws CloneNotSupportedException {
        System.out.println("\n4. 性能对比测试");
        System.out.println("=".repeat(40));
        
        int count = 5;
        
        // 测试new创建
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < count; i++) {
            Resume resume = new Resume("测试", 25, "本科");
        }
        long newTime = System.currentTimeMillis() - startTime;
        System.out.println("使用new创建" + count + "个对象耗时: " + newTime + "ms");
        
        // 测试克隆创建
        Resume template = new Resume("模板", 25, "本科");
        startTime = System.currentTimeMillis();
        for (int i = 0; i < count; i++) {
            Resume resume = template.clone();
            resume.setName("克隆" + i);
        }
        long cloneTime = System.currentTimeMillis() - startTime;
        System.out.println("使用clone创建" + count + "个对象耗时: " + cloneTime + "ms");
        
        System.out.println("性能提升: " + (newTime - cloneTime) + "ms (" + 
            String.format("%.1f", (double)(newTime - cloneTime) / newTime * 100) + "%)");
    }
    
    /**
     * 5. 序列化深克隆
     */
    private static void demonstrateSerializationClone() 
            throws IOException, ClassNotFoundException {
        System.out.println("\n5. 序列化深克隆演示");
        System.out.println("=".repeat(40));
        
        Resume original = new Resume("序列化测试", 30, "博士");
        original.setWorkExperience("研究院", "研究员", 8);
        original.addSkill("人工智能");
        original.addSkill("机器学习");
        original.getWorkExperience().addProject("智能推荐系统");
        
        System.out.println("原始对象:");
        original.display();
        
        // 序列化深克隆
        Resume serializedClone = original.deepCloneBySerialization();
        serializedClone.setName("序列化克隆");
        serializedClone.addSkill("深度学习");
        serializedClone.getWorkExperience().addProject("图像识别");
        
        System.out.println("序列化克隆对象:");
        serializedClone.display();
        
        System.out.println("原始对象(验证是否受影响):");
        original.display();
    }
}

5. 原型模式的扩展应用

5.1 与工厂模式结合

复制代码
/**
 * 原型工厂
 * 结合工厂模式和原型模式
 */
public class PrototypeFactory {
    
    public enum ResumeType {
        JUNIOR_DEVELOPER,
        SENIOR_DEVELOPER,
        PROJECT_MANAGER,
        TECH_LEAD
    }
    
    private static final Map<ResumeType, Resume> prototypes = new HashMap<>();
    
    static {
        prototypes.put(ResumeType.JUNIOR_DEVELOPER, 
            createResume("初级开发", 22, "本科", 1));
        prototypes.put(ResumeType.SENIOR_DEVELOPER, 
            createResume("高级开发", 30, "硕士", 5));
        prototypes.put(ResumeType.PROJECT_MANAGER, 
            createResume("项目经理", 35, "硕士", 8));
        prototypes.put(ResumeType.TECH_LEAD, 
            createResume("技术负责人", 40, "博士", 10));
    }
    
    private static Resume createResume(String name, int age, 
                                      String education, int experience) {
        Resume resume = new Resume(name, age, education);
        resume.setWorkExperience("科技公司", getPositionByType(name), experience);
        return resume;
    }
    
    private static String getPositionByType(String type) {
        // 根据类型返回职位
        return type;
    }
    
    public static Resume createResume(ResumeType type) throws CloneNotSupportedException {
        Resume prototype = prototypes.get(type);
        if (prototype != null) {
            return prototype.clone();
        }
        throw new IllegalArgumentException("未知的简历类型: " + type);
    }
    
    public static Resume createDeepResume(ResumeType type) throws CloneNotSupportedException {
        Resume prototype = prototypes.get(type);
        if (prototype != null) {
            return prototype.deepClone();
        }
        throw new IllegalArgumentException("未知的简历类型: " + type);
    }
}

5.2 线程安全的原型模式

复制代码
/**
 * 线程安全的原型注册表
 */
public class ThreadSafePrototypeRegistry {
    
    private final Map<String, IPrototype> prototypes = new ConcurrentHashMap<>();
    private final ReadWriteLock lock = new ReentrantReadWriteLock();
    
    /**
     * 获取原型(线程安全)
     */
    public IPrototype getPrototype(String key) throws CloneNotSupportedException {
        lock.readLock().lock();
        try {
            IPrototype prototype = prototypes.get(key);
            if (prototype == null) {
                throw new IllegalArgumentException("原型不存在: " + key);
            }
            return prototype.clone();
        } finally {
            lock.readLock().unlock();
        }
    }
    
    /**
     * 注册原型(线程安全)
     */
    public void registerPrototype(String key, IPrototype prototype) {
        lock.writeLock().lock();
        try {
            prototypes.put(key, prototype);
        } finally {
            lock.writeLock().unlock();
        }
    }
}

6. 最佳实践建议

6.1 何时使用原型模式

场景 推荐实现 注意事项
对象创建成本高 使用原型模式 确保克隆比创建快
需要大量相似对象 使用原型注册表 注意内存占用
对象状态复杂 使用深克隆 处理好循环引用
需要避免构造约束 使用复制构造函数 保持代码清晰

6.2 克隆方法选择指南

复制代码
public class CloneMethodSelector {
    
    /**
     * 根据场景选择克隆方法
     */
    public enum CloneScenario {
        SIMPLE_OBJECT,      // 简单对象,无引用
        COMPLEX_OBJECT,     // 复杂对象,有引用
        PERFORMANCE_CRITICAL, // 性能关键
        THREAD_SAFE,        // 线程安全
        SERIALIZABLE        // 需要序列化
    }
    
    /**
     * 推荐的克隆方法
     */
    public static CloneMethod getRecommendedMethod(CloneScenario scenario) {
        switch (scenario) {
            case SIMPLE_OBJECT:
                return CloneMethod.SHALLOW_CLONE;
            case COMPLEX_OBJECT:
                return CloneMethod.DEEP_CLONE_MANUAL;
            case PERFORMANCE_CRITICAL:
                return CloneMethod.COPY_CONSTRUCTOR;
            case THREAD_SAFE:
                return CloneMethod.THREAD_SAFE_CLONE;
            case SERIALIZABLE:
                return CloneMethod.SERIALIZATION_CLONE;
            default:
                return CloneMethod.DEEP_CLONE_MANUAL;
        }
    }
    
    public enum CloneMethod {
        SHALLOW_CLONE,          // 浅克隆
        DEEP_CLONE_MANUAL,      // 手动深克隆
        DEEP_CLONE_SERIALIZATION, // 序列化深克隆
        COPY_CONSTRUCTOR,       // 复制构造函数
        THREAD_SAFE_CLONE       // 线程安全克隆
    }
}

7. 注意事项和常见问题

7.1 常见问题及解决方案

问题 原因 解决方案
浅克隆引用共享 引用类型只复制引用 实现深克隆
循环引用 对象相互引用 使用序列化或记录已克隆对象
性能问题 深克隆开销大 使用延迟复制或享元模式
线程安全问题 多线程访问原型 使用线程安全容器

7.2 代码规范建议

  1. 实现Cloneable接口:明确表明类支持克隆

  2. 重写clone()方法:访问修饰符改为public

  3. 提供深克隆选项:根据需要提供深克隆方法

  4. 文档说明:清晰说明克隆行为

  5. 单元测试:测试浅克隆和深克隆的正确性

8. 总结

原型模式是创建型模式中一种重要且实用的设计模式,特别适用于以下场景:

  1. 性能优化:当对象创建成本较高时

  2. 简化创建:避免复杂的对象初始化过程

  3. 动态配置:运行时决定对象的类型

  4. 状态保存:保存和恢复对象状态

在实际应用中,应根据具体需求选择合适的克隆策略,并结合其他设计模式(如工厂模式、建造者模式)以获得更好的设计效果。通过合理使用原型模式,可以显著提高系统性能,简化代码结构,提高代码的可维护性。

相关推荐
寻星探路2 小时前
【算法通关】双指针技巧深度解析:从基础到巅峰(Java 最优解)
java·开发语言·人工智能·python·算法·ai·指针
小北方城市网2 小时前
微服务接口设计实战指南:高可用、易维护的接口设计原则与规范
java·大数据·运维·python·微服务·fastapi·数据库架构
什么都不会的Tristan2 小时前
HttpClient
java·微信登录
隐退山林2 小时前
JavaEE:多线程初阶(二)
java·开发语言·jvm
乌暮2 小时前
JavaEE初阶---《JUC 并发编程完全指南:组件用法、原理剖析与面试应答》
java·开发语言·后端·学习·面试·java-ee
6***A6632 小时前
SpringSecurity+jwt实现权限认证功能
java
野生技术架构师2 小时前
Spring Boot 4.0 预览版深度解析
java·spring boot·后端
左绍骏2 小时前
01.学习预备
android·java·学习
GISer_Jing2 小时前
Nano Banana+LoveArt三大核心功能解析:重构AI设计全链路,让创意落地更高效
人工智能·设计模式·aigc