模式定义

原型模式是一种创建型设计模式,它允许一个对象再创建另外一个可定制的对象,而无需知道任何创建的细节。原型模式通过复制现有实例来创建新实例,避免了重复创建对象的复杂性。

适用场景

结构图

graph TD A[Client] --> B{clone} B --> C[Prototype] C --> D{clone} E[ConcretePrototype] --> F{clone} F --> G[clone]

实现示例

Java 实现

// 抽象原型类
public abstract class Prototype implements Cloneable {
    protected String id;
    
    public Prototype(String id) {
        this.id = id;
    }
    
    public String getId() {
        return id;
    }
    
    public void setId(String id) {
        this.id = id;
    }
    
    public abstract Prototype clone();
}

// 具体原型类
public class ConcretePrototype extends Prototype {
    public ConcretePrototype(String id) {
        super(id);
    }
    
    @Override
    public Prototype clone() {
        try {
            return (Prototype) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new RuntimeException("克隆失败", e);
        }
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        // 创建原型对象
        Prototype prototype = new ConcretePrototype("原型对象");
        
        // 通过克隆创建新对象
        Prototype clone = prototype.clone();
        clone.setId("克隆对象");
        
        System.out.println("原型对象ID: " + prototype.getId());
        System.out.println("克隆对象ID: " + clone.getId());
    }
}
                

经典案例

1. Spring框架中的原型Bean

Spring框架中的原型(Prototype)作用域Bean是原型模式的典型应用:

// Spring中的原型Bean配置
@Component
@Scope("prototype") // 声明为原型作用域
public class UserService {
    private String userId;
    private List permissions;
    
    // 构造函数
    public UserService() {
        this.permissions = new ArrayList<>();
    }
    
    // getter和setter方法
    public String getUserId() { return userId; }
    public void setUserId(String userId) { this.userId = userId; }
    public List getPermissions() { return permissions; }
    public void setPermissions(List permissions) { this.permissions = permissions; }
    
    public void addUserPermission(String permission) {
        this.permissions.add(permission);
    }
}

// 配置类方式声明原型Bean
@Configuration
public class AppConfig {
    
    @Bean
    @Scope("prototype")
    public ReportGenerator reportGenerator() {
        return new ReportGenerator();
    }
}

// 使用原型Bean
@Service
public class BusinessService {
    
    @Autowired
    private ApplicationContext context;
    
    public void processUser(String userId) {
        // 每次都获取一个新的UserService实例
        UserService userService = context.getBean(UserService.class);
        userService.setUserId(userId);
        userService.addUserPermission("READ");
        
        // 处理用户逻辑...
        System.out.println("处理用户: " + userService.getUserId());
    }
}

2. Java中的Cloneable接口

Java标准库中的许多类都实现了Cloneable接口,支持原型模式:

// Java标准库中的原型模式应用示例
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class JavaPrototypeExample {
    public static void main(String[] args) {
        // ArrayList实现了Cloneable接口
        List originalList = new ArrayList<>();
        originalList.add("元素1");
        originalList.add("元素2");
        originalList.add("元素3");
        
        // 通过clone方法创建副本
        List clonedList = (List) ((ArrayList) originalList).clone();
        
        // 修改克隆列表不会影响原始列表
        clonedList.add("元素4");
        
        System.out.println("原始列表: " + originalList); // [元素1, 元素2, 元素3]
        System.out.println("克隆列表: " + clonedList);   // [元素1, 元素2, 元素3, 元素4]
        
        // HashMap也实现了Cloneable接口
        Map originalMap = new HashMap<>();
        originalMap.put("键1", 1);
        originalMap.put("键2", 2);
        
        // 通过clone方法创建副本
        Map clonedMap = (Map) ((HashMap) originalMap).clone();
        
        // 修改克隆Map不会影响原始Map
        clonedMap.put("键3", 3);
        
        System.out.println("原始Map: " + originalMap); // {键1=1, 键2=2}
        System.out.println("克隆Map: " + clonedMap);   // {键1=1, 键2=2, 键3=3}
    }
}

// 自定义实现Cloneable的类
class Employee implements Cloneable {
    private String name;
    private int age;
    private List skills;
    
    public Employee(String name, int age) {
        this.name = name;
        this.age = age;
        this.skills = new ArrayList<>();
    }
    
    // 浅克隆实现
    @Override
    public Employee clone() throws CloneNotSupportedException {
        return (Employee) super.clone();
    }
    
    // 深克隆实现
    public Employee deepClone() throws CloneNotSupportedException {
        Employee cloned = (Employee) super.clone();
        cloned.skills = new ArrayList<>(this.skills); // 深拷贝skills列表
        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 List getSkills() { return skills; }
    public void setSkills(List skills) { this.skills = skills; }
    public void addSkill(String skill) { this.skills.add(skill); }
}

// 使用示例
class EmployeeCloneExample {
    public static void main(String[] args) throws CloneNotSupportedException {
        Employee original = new Employee("张三", 30);
        original.addSkill("Java");
        original.addSkill("Spring");
        
        // 浅克隆
        Employee shallowClone = original.clone();
        shallowClone.setName("李四");
        shallowClone.addSkill("Python"); // 这会影响原始对象,因为skills是共享的
        
        System.out.println("浅克隆后:");
        System.out.println("原始员工技能: " + original.getSkills());     // [Java, Spring, Python]
        System.out.println("克隆员工技能: " + shallowClone.getSkills()); // [Java, Spring, Python]
        
        // 深克隆
        Employee deepClone = original.deepClone();
        deepClone.setName("王五");
        deepClone.addSkill("JavaScript");
        
        System.out.println("\n深克隆后:");
        System.out.println("原始员工技能: " + original.getSkills());   // [Java, Spring, Python]
        System.out.println("克隆员工技能: " + deepClone.getSkills()); // [Java, Spring, Python, JavaScript]
    }
}

3. 游戏开发中的原型模式

在游戏开发中,原型模式常用于创建大量的相似对象:

// 游戏中的敌人原型
abstract class Enemy implements Cloneable {
    protected String name;
    protected int health;
    protected int attackPower;
    protected String weapon;
    
    public abstract void attack();
    public abstract void move();
    
    // 克隆方法
    public Enemy clone() throws CloneNotSupportedException {
        return (Enemy) super.clone();
    }
    
    // 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 getAttackPower() { return attackPower; }
    public void setAttackPower(int attackPower) { this.attackPower = attackPower; }
    public String getWeapon() { return weapon; }
    public void setWeapon(String weapon) { this.weapon = weapon; }
}

// 具体的敌人类
class Orc extends Enemy {
    public Orc() {
        this.name = "兽人";
        this.health = 100;
        this.attackPower = 20;
        this.weapon = "斧头";
    }
    
    @Override
    public void attack() {
        System.out.println(name + "挥舞" + weapon + "攻击,造成" + attackPower + "点伤害");
    }
    
    @Override
    public void move() {
        System.out.println(name + "缓慢地移动");
    }
}

class Goblin extends Enemy {
    public Goblin() {
        this.name = "哥布林";
        this.health = 50;
        this.attackPower = 10;
        this.weapon = "短剑";
    }
    
    @Override
    public void attack() {
        System.out.println(name + "用" + weapon + "快速攻击,造成" + attackPower + "点伤害");
    }
    
    @Override
    public void move() {
        System.out.println(name + "敏捷地移动");
    }
}

// 敌人工厂使用原型模式
class EnemyFactory {
    private Map prototypes = new HashMap<>();
    
    public EnemyFactory() {
        // 初始化原型对象
        prototypes.put("orc", new Orc());
        prototypes.put("goblin", new Goblin());
    }
    
    public Enemy createEnemy(String type) throws CloneNotSupportedException {
        Enemy prototype = prototypes.get(type);
        if (prototype != null) {
            return prototype.clone();
        }
        throw new IllegalArgumentException("未知的敌人类型: " + type);
    }
}

// 游戏场景使用示例
public class GamePrototypeExample {
    public static void main(String[] args) {
        EnemyFactory factory = new EnemyFactory();
        
        try {
            // 创建大量敌人对象
            List enemies = new ArrayList<>();
            
            // 创建5个兽人
            for (int i = 0; i < 5; i++) {
                Enemy orc = factory.createEnemy("orc");
                orc.setName("兽人" + (i + 1));
                enemies.add(orc);
            }
            
            // 创建10个哥布林
            for (int i = 0; i < 10; i++) {
                Enemy goblin = factory.createEnemy("goblin");
                goblin.setName("哥布林" + (i + 1));
                enemies.add(goblin);
            }
            
            // 让所有敌人行动
            for (Enemy enemy : enemies) {
                enemy.move();
                enemy.attack();
            }
            
            System.out.println("总共创建了 " + enemies.size() + " 个敌人");
            
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
}

优缺点

优点

  • 性能提高,直接在内存中拷贝,比new一个对象性能要好
  • 逃避构造函数的约束,直接在内存中拷贝,不执行构造函数
  • 简化对象的创建过程,使得创建对象就像复制粘贴一样简单

缺点

  • 配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类不是很难,但对于已有的类不一定很容易
  • 必须实现Cloneable接口
  • 在实现深克隆时可能需要编写较为复杂的代码