跳转至

原型模式

分类:创建型模式。

类结构

classDiagram
    direction TB
    class Client {
        +duplicate()
    }
    class Prototype {
        <<interface>>
        +clone() Prototype
    }
    class ConcretePrototypeA {
        +clone() Prototype
    }
    class ConcretePrototypeB {
        +clone() Prototype
    }
    Client --> Prototype : 请求复制
    Prototype <|.. ConcretePrototypeA
    Prototype <|.. ConcretePrototypeB

角色定义

角色 定义
Client(客户) 持有原型引用,通过复制现有对象获得新对象。
Prototype(原型接口) 声明返回对象副本的复制操作。
ConcretePrototypeA / ConcretePrototypeB(具体原型) 实现自身的复制逻辑,决定如何复制内部状态和引用;深拷贝或浅拷贝由具体实现约定。

Head First 示例:复制已有的怪物配置

本节对应《Head First 设计模式》附录中的原型主题,具体类型与行为按作者 2020 年第二版配套代码 prototype 下的 Java 实现整理。在需要沿用某只怪物的名称、翅膀、头数和喷火能力时,调用者可以通过 Monster.copy() 复制已有配置,再为副本改名。Dragon 与 Drakon 都继承 Monster,各自实现复制操作;图中的名称来自配套源码,类图是本节据此整理的示意。

Monster 实现 Java 的标记接口 Cloneable,并声明抽象的 copy();两个子类的实现都调用继承的 clone(),因此这里执行的是浅克隆。Cloneable 本身没有声明 copy()。同目录的 Client.java 使用 Prototype、ConcretePrototype1 和 ConcretePrototype2,没有调用这组怪物类;以下协作过程是依据怪物 Java 类整理的调用说明,同目录网页中的脚本不作为 Java 克隆流程的依据。

具体类名 对应角色 职责
Cloneable Java 克隆标记接口 标记实例支持通过 Object.clone() 进行字段复制,本身没有复制方法。
Monster 抽象原型 保存怪物状态,声明 copy(),提供 setName() 和默认不执行动作的 spitPoison()。
Dragon 具体原型 构造时设置是否有翅膀,并开启喷火能力;实现 copy() 返回副本。
Drakon 具体原型 构造时设置头数和喷火能力,重写 spitPoison(),并实现 copy()。

示例 UML

图中保留 4 个类型,省略 Object、异常类型、部分状态字段及文字输出方法。~ 表示包级可见,* 标记抽象方法。copy() 的三个声明都带有 throws CloneNotSupportedException,图内为简洁未列出异常声明;两个具体实现的返回类型均为 Monster。

classDiagram
    direction TB
    class Cloneable {
        <<interface>>
    }
    class Monster {
        <<abstract>>
        ~String name
        ~boolean hasWings
        ~int numHeads
        ~boolean canBreatheFire
        +Monster(String name)
        +copy() Monster*
        +setName(String name) void
        +spitPoison() void
    }
    class Dragon {
        +Dragon(String name, boolean hasWings)
        +copy() Monster
    }
    class Drakon {
        +Drakon(String name, int numHeads, boolean canBreatheFire)
        +copy() Monster
        +spitPoison() void
    }
    Cloneable <|.. Monster
    Monster <|-- Dragon
    Monster <|-- Drakon

协作过程

  1. 调用者先通过构造器创建 Dragon 或 Drakon 作为原型,并用 Monster 引用保存它。例如,Drakon 的构造参数可以确定名称、头数和是否喷火。
  2. 调用者请求 copy(),并按其方法声明处理或继续抛出 CloneNotSupportedException。动态分派进入实际子类的复制实现,后者调用 clone() 并返回新的 Monster 引用。
  3. 副本是独立对象,保留原对象的实际运行时类型和字段值;复制 Drakon 得到的仍是 Drakon,因此它仍使用重写的 spitPoison()。
  4. 调用副本的 setName() 只会替换副本的名称引用,不会改变原型名称。当前字段主要是基本类型和不可变的 String;如果以后加入可变引用字段,现有浅克隆会共享这些对象,需要另行决定是否深拷贝。

固定版本源码:

评论