JavaDog程序狗
发布于 2026-11-03 / 1 阅读
0
0

【设计模式】跟着狗哥复习设计模式(五):原型模式

狗哥复印店里有一份简历模板,姓名、岗位和工作经历都填好了。

小明来做简历,内容跟模板差不多,只需要换个姓名。要是再拿一张空白纸重填,多少有点费劲。

Java 里也会遇到这种情况。已经有一个合适的对象,复制一份再改,通常更省事。

加瓦狗在复印店复制简历

每次重新 new,有点重复

先不用设计模式,直接创建两份简历:

Resume dogResume = new Resume(
        "狗哥", "Java 开发", "3 年项目经验"
);

Resume xiaomingResume = new Resume(
        "小明", "Java 开发", "3 年项目经验"
);

代码没错。不过后两项明明一样,又写了一遍。

字段再多些,重复就更明显。改模板时,另一份还可能漏改,挺麻烦。

从空白页重写和复制模板的对比

原型模式的思路很朴素:拿现成对象当模板,复制出一个新对象。

先让 Resume 可以复制

Java 里常见的标准写法,是让类实现 Cloneable。

public class Resume implements Cloneable {
    private String name;
    private final String job;
    private final String experience;

    public Resume(String name, String job, String experience) {
        this.name = name;
        this.job = job;
        this.experience = experience;
    }
}

Cloneable 像个标记,表示这个类允许调用 Object 提供的复制能力。

原型对象和副本之间的关系

这里的原型,就是已经填好的 Resume。副本是从它复制出来的新简历。

把 clone() 公开出来

Object 里的 clone() 是 protected,外部不能直接调用。

所以在 Resume 里重写一次,把它改成 public:

@Override
public Resume clone() {
    try {
        return (Resume) super.clone();
    } catch (CloneNotSupportedException exception) {
        throw new AssertionError(exception);
    }
}

super.clone() 负责复制当前对象。

返回值转成 Resume,调用的人就不用自己做强制类型转换。

clone 方法的调用过程

这里捕获了 CloneNotSupportedException。咱们的类已经实现 Cloneable,正常不会走到异常分支,所以转成 AssertionError。

别被这几行吓着,真正干复制工作的就一句:

return (Resume) super.clone();

复制一份,再改姓名

现在可以直接拿狗哥的简历做模板:

Resume original = new Resume(
        "狗哥", "Java 开发", "3 年项目经验"
);

Resume copy = original.clone();
copy.setName("小明");

输出结果:

原简历:姓名=狗哥, 岗位=Java 开发, 经历=3 年项目经验
新简历:姓名=小明, 岗位=Java 开发, 经历=3 年项目经验

岗位和经历直接沿用模板,只改姓名就行。

修改副本不会影响原简历

还有个重点:original 和 copy 不是同一个对象。

修改副本的姓名,原简历还是狗哥。各是各的,清清楚楚。

浅拷贝,先记住一个小坑

上面的三个字段都是字符串,复制起来看着很省心。

但 super.clone() 默认做的是 浅拷贝。对象里如果放了可修改的 List,原对象和副本可能还指向同一个列表。

浅拷贝时原对象和副本可能共享列表

比如简历里再加一个技能列表:

private List<String> skills;

只用 super.clone(),副本往 skills 里添加内容时,原简历也可能跟着变化。

解决办法是复制对象后,再给副本创建一份新列表:

Resume copy = (Resume) super.clone();
copy.skills = new ArrayList<>(this.skills);
return copy;

这篇先知道边界就够了。主例子里没有 List,不用一下子绕进去。

原型模式适合啥时候

没必要让所有对象都实现 Cloneable。

只有两三个字段,直接 new 已经很明白,再加 clone() 反而显得绕。

什么时候适合使用原型模式

我一般会在这些情况下考虑:

  • 已经有一个比较完整的模板对象;
  • 新对象和模板大部分字段相同;
  • 从头创建步骤多,容易漏掉配置;
  • 业务里经常需要“复制一份再改”。

像简历模板、表单模板、游戏角色初始配置,都比较好理解。

狗哥收个尾

原型模式做的事不复杂:保留一个现成对象,需要时复制一份。

Java 的常见写法是实现 Cloneable,再公开重写 clone()。

对象里只有字符串这类字段,例子很好懂。碰到 List 之类的可变对象,多看一眼浅拷贝,别让两份数据串到一起。

下一篇咱聊 适配器模式。有些接口对不上,怎么加个小转换层,让旧代码接着用。


评论