Java设计模式精讲---04原型模式

你是否遇到过这样的场景:需要创建大量结构相似、仅少数属性不同的对象(比如 100 个游戏角色,基础属性相同,仅昵称和装备有差异)?如果每次都通过new关键字从头初始化,不仅代码冗余,还会因为重复执行构造函数中的复杂逻辑(比如数据库查询、网络请求)浪费性能。

这时候,原型模式(Prototype Pattern) 就能派上用场 ------ 它的核心思想是:通过复制现有对象(原型)来创建新对象,而非重新初始化,就像现实中 "复制粘贴" 文档再修改细节,既高效又省力。

一、核心概念:克隆,而非新建

原型模式的本质是 "对象克隆",它通过让对象自身提供复制方法,将对象的创建过程从 "主动新建" 转为 "被动复制"。关键角色只有两个:

  • 原型接口(Prototype) :定义克隆方法(比如 Java 中的Cloneable接口,虽然是标记接口,但约定了clone()方法);
  • 具体原型(Concrete Prototype):实现克隆方法,完成自身复制。
二、实现方式:浅克隆 vs 深克隆

克隆的核心是 "复制对象的属性",但根据属性类型(基本类型 / 引用类型),克隆分为两种:

1. 浅克隆:只复制 "表面"

浅克隆会复制对象的基本类型属性(int、String 等),但对于引用类型属性(比如对象、集合),仅复制引用地址 ------ 新对象和原对象的引用属性会指向同一个内存地址,修改其中一个会影响另一个。

Java 实现示例 (基于Cloneable接口):

java 复制代码
​
// 具体原型:游戏角色
class GameRole implements Cloneable {
    private String name; // 基本类型
    private int level;   // 基本类型
    private Equipment equipment; // 引用类型(装备)

    // 构造函数(模拟复杂初始化逻辑)
    public GameRole(String name, int level, Equipment equipment) {
        this.name = name;
        this.level = level;
        this.equipment = equipment;
        System.out.println("执行复杂初始化(比如加载角色模型)...");
    }

    // 实现克隆方法(浅克隆)
    @Override
    protected GameRole clone() throws CloneNotSupportedException {
        return (GameRole) super.clone(); // Object.clone()默认是浅克隆
    }

    // getter/setter省略
}

// 引用类型:装备
class Equipment {
    private String weapon;
    public Equipment(String weapon) { this.weapon = weapon; }
    // getter/setter省略
}

// 测试浅克隆
public class PrototypeDemo {
    public static void main(String[] args) throws CloneNotSupportedException {
        // 创建原型对象(执行一次复杂初始化)
        Equipment equip = new Equipment("木剑");
        GameRole prototype = new GameRole("原型角色", 1, equip);

        // 克隆出3个新角色(无需重复初始化)
        GameRole role1 = prototype.clone();
        role1.setName("玩家1");
        GameRole role2 = prototype.clone();
        role2.setName("玩家2");
        GameRole role3 = prototype.clone();
        role3.setName("玩家3");

        // 问题:修改原型的装备,会影响所有克隆对象(因为引用同一份)
        prototype.getEquipment().setWeapon("铁剑");
        System.out.println(role1.getEquipment().getWeapon()); // 输出"铁剑"(被影响)
    }
}

​
2. 深克隆:复制 "全部细节"

深克隆会递归复制所有属性,包括引用类型 ------ 新对象和原对象的引用属性完全独立,修改互不影响。常见实现方式有两种:

  • 手动对引用类型属性递归克隆;
  • 通过序列化(将对象转成字节流再恢复)实现(更通用)。

序列化实现深克隆示例

java 复制代码
​
import java.io.*;

// 注意:需要实现Serializable接口
class GameRole implements Cloneable, Serializable {
    private String name;
    private int level;
    private Equipment equipment; // 引用类型也需实现Serializable

    // 深克隆:通过序列化
    public GameRole deepClone() throws IOException, ClassNotFoundException {
        // 1. 序列化:将对象写入字节流
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(this);

        // 2. 反序列化:从字节流恢复对象(新对象)
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        return (GameRole) ois.readObject();
    }
    // 其他代码省略
}

// 装备类也需实现Serializable
class Equipment implements Serializable {
    private String weapon;
    // 代码省略
}

// 测试深克隆
public class DeepCloneDemo {
    public static void main(String[] args) throws Exception {
        Equipment equip = new Equipment("木剑");
        GameRole prototype = new GameRole("原型角色", 1, equip);

        GameRole role1 = prototype.deepClone();
        role1.setName("玩家1");

        // 修改原型的装备,克隆对象不受影响
        prototype.getEquipment().setWeapon("铁剑");
        System.out.println(role1.getEquipment().getWeapon()); // 输出"木剑"(独立)
    }
}

​
三、适合用原型模式的 3 类场景
  1. 对象创建成本高 :如果对象构造需要大量资源(比如数据库查询、文件 IO),克隆比new更高效(比如报表系统中,批量生成结构相同、数据略有差异的报表);
  2. 需要动态生成对象:比如插件系统,无法提前知道要创建的对象类型,可通过克隆已加载的插件实例扩展功能;
  3. 避免构造函数副作用:如果构造函数中有非初始化逻辑(比如注册监听器),重复调用可能导致意外(克隆可跳过构造函数)。
四、避坑指南
  • 浅克隆可能导致 "意外共享":如果对象包含引用类型,优先用深克隆(除非明确需要共享引用);
  • 序列化深克隆的限制:被克隆的类及所有引用类型必须实现Serializable, transient 修饰的属性不会被克隆;
  • 克隆与构造函数:Object.clone()不会调用构造函数,若初始化逻辑依赖构造函数,需在克隆后手动补充。
总结

原型模式是 "高效复制" 的代名词 ------ 当你需要批量创建相似对象时,与其重复new和初始化,不如 "克隆原型 + 修改细节"。记住:浅克隆适合简单对象,深克隆适合含引用类型的复杂对象,根据场景选择即可。

相关推荐
秋风&萧瑟2 小时前
【C++】智能指针介绍
java·c++·算法
QiZhang | UESTC2 小时前
JAVA算法练习题day67
java·python·学习·算法·leetcode
毕设源码-朱学姐2 小时前
【开题答辩全过程】以 基于java的民宿管理小程序为例,包含答辩的问题和答案
java·开发语言·小程序
それども2 小时前
List 添加元素提示 UnsupportedOperationException
java
ᐇ9592 小时前
Java集合框架:深入理解List与Set及其实现类
java·开发语言
无名-CODING2 小时前
Java集合List详解:从入门到精通
java·windows·list
laplace01232 小时前
JAVA-Redis上
java·redis·spring
不要喷香水2 小时前
26.java openCV4.x 入门-Imgproc之图像尺寸调整与区域提取
java·人工智能·opencv·计算机视觉
脸大是真的好~3 小时前
黑马JAVAWeb - SpringAOP
java