23种设计模式23种设计模式
首页
介绍
  • 单例模式
  • 工厂方法模式
  • 抽象工厂模式
  • 建造者模式
  • 原型模式
  • 适配器模式
  • 桥接模式
  • 组合模式
  • 装饰器模式
  • 外观模式
  • 享元模式
  • 代理模式
  • 责任链模式
  • 命令模式
  • 解释器模式
  • 迭代器模式
  • 中介者模式
  • 备忘录模式
  • 观察者模式
  • 状态模式
  • 策略模式
  • 模板方法模式
  • 访问者模式
🚀 编程指南
首页
介绍
  • 单例模式
  • 工厂方法模式
  • 抽象工厂模式
  • 建造者模式
  • 原型模式
  • 适配器模式
  • 桥接模式
  • 组合模式
  • 装饰器模式
  • 外观模式
  • 享元模式
  • 代理模式
  • 责任链模式
  • 命令模式
  • 解释器模式
  • 迭代器模式
  • 中介者模式
  • 备忘录模式
  • 观察者模式
  • 状态模式
  • 策略模式
  • 模板方法模式
  • 访问者模式
🚀 编程指南
  • 创建型模式

    • 单例模式 (Singleton)
    • 工厂方法模式 (Factory Method)
    • 抽象工厂模式 (Abstract Factory)
    • 建造者模式 (Builder)
    • 原型模式 (Prototype)

原型模式 (Prototype)

📖 通俗理解

想象你需要填写10份一模一样的申请表:

  • 笨方法:每张表都从头开始填,写10遍姓名、地址、电话...
  • 聪明方法:填好一份,然后复印9份,只改不同的地方

原型模式就是:通过复制已有对象来创建新对象,而不是重新创建。

🎯 解决什么问题?

问题场景:

  1. 创建对象成本很高(如从数据库加载、复杂计算)
  2. 需要创建很多相似的对象
  3. 对象的创建过程很复杂

解决方案:先创建一个"原型"对象,需要新对象时直接复制它。

🌰 生活中的例子

  • 复印文件:不用重新打印,复印就行
  • 细胞分裂:通过复制自己产生新细胞
  • 游戏存档:复制一个存档点
  • 邮件模板:复制模板,修改收件人

💻 Java 代码实现

Java 中实现原型模式很简单,实现 Cloneable 接口,重写 clone() 方法即可。

方式一:浅拷贝

/**
 * 简历类
 */
public class Resume implements Cloneable {
    private String name;
    private int age;
    private String education;
    private String workExperience;
    
    public Resume(String name) {
        this.name = name;
    }
    
    public void setPersonalInfo(int age, String education) {
        this.age = age;
        this.education = education;
    }
    
    public void setWorkExperience(String workExperience) {
        this.workExperience = workExperience;
    }
    
    public void display() {
        System.out.println("=== 简历 ===");
        System.out.println("姓名: " + name);
        System.out.println("年龄: " + age);
        System.out.println("学历: " + education);
        System.out.println("工作经历: " + workExperience);
    }
    
    // 克隆方法
    @Override
    public Resume clone() {
        try {
            return (Resume) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new RuntimeException(e);
        }
    }
}

使用方式:

public class Client {
    public static void main(String[] args) {
        // 创建原型简历
        Resume prototype = new Resume("张三");
        prototype.setPersonalInfo(25, "本科");
        prototype.setWorkExperience("2020-2023: ABC 公司 Java 开发");
        
        // 复制简历,投递给不同公司
        Resume resume1 = prototype.clone();
        resume1.setWorkExperience("2020-2023: ABC 公司 Java 开发\n求职意向: 阿里巴巴");
        
        Resume resume2 = prototype.clone();
        resume2.setWorkExperience("2020-2023: ABC 公司 Java 开发\n求职意向: 腾讯");
        
        Resume resume3 = prototype.clone();
        resume3.setWorkExperience("2020-2023: ABC 公司 Java 开发\n求职意向: 字节跳动");
        
        // 显示所有简历
        resume1.display();
        System.out.println();
        resume2.display();
        System.out.println();
        resume3.display();
    }
}

🚨 浅拷贝的问题

浅拷贝只复制对象本身,不复制对象内部的引用对象!

public class ShallowCopyProblem {
    public static void main(String[] args) {
        // 原型对象
        Person prototype = new Person("张三");
        prototype.setAddress(new Address("北京", "朝阳区"));
        
        // 浅拷贝
        Person copy = prototype.clone();
        
        // 修改拷贝对象的地址
        copy.getAddress().setCity("上海");
        
        // 问题:原型对象的地址也被改了!
        System.out.println("原型地址: " + prototype.getAddress().getCity()); // 上海
        System.out.println("拷贝地址: " + copy.getAddress().getCity());      // 上海
    }
}

方式二:深拷贝(推荐)

深拷贝会递归复制所有引用对象。

方法1:手动深拷贝

/**
 * 地址类
 */
public class Address implements Cloneable {
    private String city;
    private String district;
    
    public Address(String city, String district) {
        this.city = city;
        this.district = district;
    }
    
    // getter 和 setter...
    
    @Override
    public Address clone() {
        try {
            return (Address) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new RuntimeException(e);
        }
    }
}

/**
 * 员工类 - 深拷贝实现
 */
public class Employee implements Cloneable {
    private String name;
    private int age;
    private Address address;  // 引用类型
    
    public Employee(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    // getter 和 setter...
    
    // 深拷贝:手动克隆引用对象
    @Override
    public Employee clone() {
        try {
            Employee cloned = (Employee) super.clone();
            // 关键:对引用类型也进行克隆
            if (this.address != null) {
                cloned.address = this.address.clone();
            }
            return cloned;
        } catch (CloneNotSupportedException e) {
            throw new RuntimeException(e);
        }
    }
}

测试深拷贝:

public class DeepCopyTest {
    public static void main(String[] args) {
        // 创建原型
        Employee prototype = new Employee("张三", 25);
        prototype.setAddress(new Address("北京", "朝阳区"));
        
        // 深拷贝
        Employee copy = prototype.clone();
        
        // 修改拷贝对象的地址
        copy.getAddress().setCity("上海");
        
        // 原型对象不受影响
        System.out.println("原型地址: " + prototype.getAddress().getCity()); // 北京
        System.out.println("拷贝地址: " + copy.getAddress().getCity());      // 上海
    }
}

方法2:序列化深拷贝(推荐)

import java.io.*;

/**
 * 使用序列化实现深拷贝
 * 所有相关类都需要实现 Serializable 接口
 */
public class DeepCopyUtil {
    
    @SuppressWarnings("unchecked")
    public static <T extends Serializable> T deepCopy(T object) {
        try {
            // 序列化到字节数组
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(object);
            oos.close();
            
            // 从字节数组反序列化
            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bis);
            T copy = (T) ois.readObject();
            ois.close();
            
            return copy;
        } catch (Exception e) {
            throw new RuntimeException("深拷贝失败", e);
        }
    }
}

// 使用
public class Client {
    public static void main(String[] args) {
        Employee prototype = new Employee("张三", 25);
        prototype.setAddress(new Address("北京", "朝阳区"));
        
        // 使用工具类深拷贝
        Employee copy = DeepCopyUtil.deepCopy(prototype);
        
        copy.getAddress().setCity("上海");
        
        System.out.println("原型: " + prototype.getAddress().getCity()); // 北京
        System.out.println("拷贝: " + copy.getAddress().getCity());      // 上海
    }
}

🔥 实战案例:游戏角色克隆

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

/**
 * 游戏角色
 */
public class GameCharacter implements Serializable, Cloneable {
    private String name;
    private int level;
    private int health;
    private int attack;
    private List<String> skills;
    private Equipment equipment;
    
    public GameCharacter(String name) {
        this.name = name;
        this.level = 1;
        this.health = 100;
        this.attack = 10;
        this.skills = new ArrayList<>();
        this.equipment = new Equipment();
    }
    
    public void addSkill(String skill) {
        skills.add(skill);
    }
    
    public void levelUp() {
        level++;
        health += 20;
        attack += 5;
    }
    
    // 深拷贝
    @Override
    public GameCharacter clone() {
        try {
            GameCharacter cloned = (GameCharacter) super.clone();
            // 克隆列表
            cloned.skills = new ArrayList<>(this.skills);
            // 克隆装备
            cloned.equipment = this.equipment.clone();
            return cloned;
        } catch (CloneNotSupportedException e) {
            throw new RuntimeException(e);
        }
    }
    
    @Override
    public String toString() {
        return String.format("角色[%s] Lv.%d HP:%d ATK:%d 技能:%s 装备:%s",
            name, level, health, attack, skills, equipment);
    }
    
    // getter 和 setter...
}

/**
 * 装备
 */
public class Equipment implements Serializable, Cloneable {
    private String weapon = "木剑";
    private String armor = "布衣";
    
    // getter 和 setter...
    
    @Override
    public Equipment clone() {
        try {
            return (Equipment) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new RuntimeException(e);
        }
    }
    
    @Override
    public String toString() {
        return String.format("[武器:%s, 防具:%s]", weapon, armor);
    }
}

使用方式:

public class GameDemo {
    public static void main(String[] args) {
        // 创建一个满级角色作为模板
        GameCharacter template = new GameCharacter("战士模板");
        for (int i = 0; i < 99; i++) {
            template.levelUp();
        }
        template.addSkill("斩击");
        template.addSkill("旋风斩");
        template.addSkill("狂暴");
        template.getEquipment().setWeapon("屠龙刀");
        template.getEquipment().setArmor("黄金甲");
        
        System.out.println("模板角色: " + template);
        
        // 快速创建多个相同配置的角色
        GameCharacter player1 = template.clone();
        player1.setName("玩家1");
        
        GameCharacter player2 = template.clone();
        player2.setName("玩家2");
        player2.addSkill("嘲讽");  // 额外技能
        
        GameCharacter player3 = template.clone();
        player3.setName("玩家3");
        player3.getEquipment().setWeapon("倚天剑");  // 不同装备
        
        System.out.println("\n克隆的角色:");
        System.out.println(player1);
        System.out.println(player2);
        System.out.println(player3);
        
        // 验证深拷贝:修改 player3 不影响模板
        System.out.println("\n模板武器: " + template.getEquipment().getWeapon()); // 屠龙刀
    }
}

📊 浅拷贝 vs 深拷贝

对比项浅拷贝深拷贝
基本类型值复制 ✅值复制 ✅
引用类型地址复制 ❌对象复制 ✅
性能快慢
实现复杂度简单复杂

✅ 适用场景

  1. 创建对象成本高:如需要复杂计算或数据库查询
  2. 需要大量相似对象:如游戏中的怪物、文档模板
  3. 保护原对象:创建副本进行操作

⚠️ 注意事项

  1. 必须实现 Cloneable 接口
  2. 注意深拷贝和浅拷贝的区别
  3. final 字段无法在 clone() 中修改

小结

原型模式的核心:通过复制已有对象来创建新对象。

关键点:

  • 浅拷贝:只复制对象本身
  • 深拷贝:递归复制所有引用对象
  • 推荐使用序列化方式实现深拷贝

👉 下一篇:适配器模式

Prev
建造者模式 (Builder)