前言

Github:https://github.com/HealerJean

博客:http://blog.healerjean.com

一、原型/复制模式

1、模式概述

原型模式 是一种 创建型设计模式,它通过 复制(克隆)现有对象 来创建新对象,而不是通过 new 调用构造函数。

核心思想“不 new,直接 clone —— 用现成的模板快速生成新实例。”

  • 当对象的创建成本较高(如需数据库查询、复杂计算、网络请求等),而对象结构又相对稳定时,使用原型模式可显著提升性能。

  • 原型模式常用于缓存预构建对象、游戏开发中的敌人/道具生成、配置模板复制等场景。

原型模式 = 缓存模板 + 克隆复用

  • 它不是“创建新东西”,而是“复制一个现成的”,在性能与灵活性之间取得平衡。

2、使用场景

原型模式适用于以下情况:

  • 创建对象的过程 代价高昂(时间、资源消耗大);
  • 系统需要 动态决定创建哪些对象,且这些对象类型在运行时才确定;
  • 对象具有 多层嵌套或复杂初始化逻辑,但结构相对固定;
  • 需要 避免重复初始化 相同配置的对象。

典型例子

  • 游戏中大量相同敌人的生成;
  • Excel 中“复制单元格格式”;
  • Spring 中的 prototype 作用域 Bean(部分实现基于克隆思想)。

3、示例程序:图形对象的克隆缓存

1)抽象原型类 Shape

  • 使用 protected 字段便于子类访问;
  • 强制转型为 Shape 提升类型安全;
  • 异常转为运行时异常,避免客户端处理 checked exception。
import lombok.Data;
import lombok.ToString;

/**
 * 抽象原型类 —— 实现 Cloneable 接口
 */
@Data
@ToString
public abstract class Shape implements Cloneable {

    protected String id;
    protected String type;

    public abstract void draw();

    @Override
    public Shape clone() {
        try {
            return (Shape) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new RuntimeException("Clone not supported for " + getClass(), e);
        }
    }
}

2)具体原型类

a、Rectangle

@Data
@ToString(callSuper = true)
public class Rectangle extends Shape {

    private String rectangleName; // 修正拼写:pectangleName → rectangleName

    public Rectangle() {
        this.type = "Rectangle";
    }

    @Override
    public void draw() {
        System.out.println(id + ":" + type + ":" + rectangleName);
    }
}

b、 Square

@Data
@ToString(callSuper = true)
public class Square extends Shape {

    private String squareName;

    public Square() {
        this.type = "Square";
    }

    @Override
    public void draw() {
        System.out.println(id + ":" + type + ":" + squareName);
    }
}

3)原型管理器 ShapeCache

import java.util.HashMap;
import java.util.Map;

/**
 * 原型管理器 —— 缓存并提供克隆对象
 */
public class ShapeCache {

    private static final Map<String, Shape> prototypeMap = new HashMap<>();

    /**
     * 注册原型
     */
    public static void register(Shape prototype) {
        prototypeMap.put(prototype.getType(), prototype);
    }

    /**
     * 获取克隆对象(全新实例)
     */
    public static Shape get(String type) {
        Shape prototype = prototypeMap.get(type);
        if (prototype == null) {
            throw new IllegalArgumentException("Prototype not found for type: " + type);
        }
        return prototype.clone();
    }
}

4)客户端测试 Main

关键验证:克隆对象是 独立副本,修改它不会影响原型缓存。

public class Main {
    public static void main(String[] args) {
        // 1. 注册原型
        Rectangle rectProto = new Rectangle();
        rectProto.setId("1");
        rectProto.setRectangleName("DefaultRect");
        ShapeCache.register(rectProto);

        Square squareProto = new Square();
        squareProto.setId("2");
        squareProto.setSquareName("DefaultSquare");
        ShapeCache.register(squareProto);

        // 2. 获取克隆对象(全新实例)
        Rectangle rect1 = (Rectangle) ShapeCache.get("Rectangle");
        rect1.draw(); // 输出: 1:Rectangle:DefaultRect

        Square square1 = (Square) ShapeCache.get("Square");
        square1.draw(); // 输出: 2:Square:DefaultSquare

        // 3. 修改克隆对象不影响原型
        rect1.setId("100");
        rect1.setRectangleName("ModifiedRect");
        rect1.draw(); // 输出: 100:Rectangle:ModifiedRect

        // 原型未变
        Rectangle original = (Rectangle) ShapeCache.get("Rectangle");
        original.draw(); // 仍输出: 1:Rectangle:DefaultRect
    }
}