原型模式 (Prototype Pattern)

原型模式 (Prototype Pattern)

概述

原型模式是一种创建型设计模式,它通过复制现有实例来创建新的实例,而不是通过new关键字创建。原型模式允许一个对象再创建另外一个可定制的对象,而无需知道如何创建的细节。

意图

  • 用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象

适用场景

  • 当要实例化的类是在运行时刻指定时,例如,通过动态装载
  • 为了避免创建一个与产品类层次平行的工厂类层次时
  • 当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些

结构

复制代码
┌─────────────┐
│  Prototype  │
├─────────────┤
│ + clone()   │
└─────────────┘
        ▲
        │
┌─────────────┐
│ConcretePrototype1│
├─────────────┤
│ + clone()   │
└─────────────┘

┌─────────────┐
│ConcretePrototype2│
├─────────────┤
│ + clone()   │
└─────────────┘

┌─────────────┐
│   Client    │
├─────────────┤
│ operation() │
└─────────────┘

参与者

  • Prototype:声明一个克隆自身的接口
  • ConcretePrototype:实现一个克隆自身的操作
  • Client:让一个原型克隆自身从而创建一个新的对象

示例代码

下面是一个完整的原型模式示例,以创建不同类型的图形为例:

java 复制代码
// Prototype - 原型接口
public interface Shape extends Cloneable {
    void draw();
    Shape clone();
}

// ConcretePrototype1 - 具体原型1
public class Circle implements Shape {
    private String color;
    private int radius;
    
    public Circle(String color, int radius) {
        this.color = color;
        this.radius = radius;
    }
    
    @Override
    public void draw() {
        System.out.println("绘制" + color + "的圆形,半径为" + radius);
    }
    
    @Override
    public Shape clone() {
        try {
            return (Shape) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            return null;
        }
    }
    
    // Getter和Setter方法
    public String getColor() {
        return color;
    }
    
    public void setColor(String color) {
        this.color = color;
    }
    
    public int getRadius() {
        return radius;
    }
    
    public void setRadius(int radius) {
        this.radius = radius;
    }
}

// ConcretePrototype2 - 具体原型2
public class Rectangle implements Shape {
    private String color;
    private int width;
    private int height;
    
    public Rectangle(String color, int width, int height) {
        this.color = color;
        this.width = width;
        this.height = height;
    }
    
    @Override
    public void draw() {
        System.out.println("绘制" + color + "的矩形,宽度为" + width + ",高度为" + height);
    }
    
    @Override
    public Shape clone() {
        try {
            return (Shape) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            return null;
        }
    }
    
    // Getter和Setter方法
    public String getColor() {
        return color;
    }
    
    public void setColor(String color) {
        this.color = color;
    }
    
    public int getWidth() {
        return width;
    }
    
    public void setWidth(int width) {
        this.width = width;
    }
    
    public int getHeight() {
        return height;
    }
    
    public void setHeight(int height) {
        this.height = height;
    }
}

// 原型管理器
public class ShapeCache {
    private static Map<String, Shape> shapeMap = new HashMap<>();
    
    public static Shape getShape(String shapeId) {
        Shape cachedShape = shapeMap.get(shapeId);
        return (cachedShape != null) ? cachedShape.clone() : null;
    }
    
    public static void loadCache() {
        Circle circle = new Circle("红色", 10);
        shapeMap.put("红色圆形", circle);
        
        Rectangle rectangle = new Rectangle("蓝色", 20, 30);
        shapeMap.put("蓝色矩形", rectangle);
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        // 初始化缓存
        ShapeCache.loadCache();
        
        // 从缓存中获取克隆对象
        Shape clonedShape1 = ShapeCache.getShape("红色圆形");
        System.out.println("形状: " + clonedShape1.getClass().getSimpleName());
        clonedShape1.draw();
        
        Shape clonedShape2 = ShapeCache.getShape("蓝色矩形");
        System.out.println("形状: " + clonedShape2.getClass().getSimpleName());
        clonedShape2.draw();
        
        // 验证克隆的对象是新的实例
        Shape clonedShape3 = ShapeCache.getShape("红色圆形");
        System.out.println("clonedShape1 == clonedShape3: " + (clonedShape1 == clonedShape3)); // false
        
        // 修改克隆对象不会影响原对象
        if (clonedShape1 instanceof Circle) {
            Circle circle1 = (Circle) clonedShape1;
            circle1.setColor("绿色");
            circle1.setRadius(15);
            circle1.draw();
            
            Circle circle3 = (Circle) clonedShape3;
            circle3.draw(); // 仍然是红色的,半径为10
        }
    }
}

深拷贝与浅拷贝

浅拷贝示例

java 复制代码
public class Book implements Cloneable {
    private String title;
    private Author author; // 引用类型
    
    public Book(String title, Author author) {
        this.title = title;
        this.author = author;
    }
    
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone(); // 浅拷贝
    }
    
    // Getter和Setter方法
    public String getTitle() {
        return title;
    }
    
    public void setTitle(String title) {
        this.title = title;
    }
    
    public Author getAuthor() {
        return author;
    }
    
    public void setAuthor(Author author) {
        this.author = author;
    }
}

public class Author {
    private String name;
    
    public Author(String name) {
        this.name = name;
    }
    
    // Getter和Setter方法
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
}

public class Client {
    public static void main(String[] args) throws CloneNotSupportedException {
        Author author = new Author("张三");
        Book book1 = new Book("Java设计模式", author);
        
        // 浅拷贝
        Book book2 = (Book) book1.clone();
        
        // 修改拷贝对象的引用类型属性
        book2.getAuthor().setName("李四");
        
        // 原对象的引用类型属性也被修改了
        System.out.println("book1的作者: " + book1.getAuthor().getName()); // 李四
        System.out.println("book2的作者: " + book2.getAuthor().getName()); // 李四
    }
}

深拷贝示例

java 复制代码
public class Book implements Cloneable {
    private String title;
    private Author author; // 引用类型
    
    public Book(String title, Author author) {
        this.title = title;
        this.author = author;
    }
    
    @Override
    protected Object clone() throws CloneNotSupportedException {
        Book cloned = (Book) super.clone();
        // 深拷贝,创建新的Author对象
        cloned.author = new Author(this.author.getName());
        return cloned;
    }
    
    // Getter和Setter方法
    public String getTitle() {
        return title;
    }
    
    public void setTitle(String title) {
        this.title = title;
    }
    
    public Author getAuthor() {
        return author;
    }
    
    public void setAuthor(Author author) {
        this.author = author;
    }
}

public class Author {
    private String name;
    
    public Author(String name) {
        this.name = name;
    }
    
    // Getter和Setter方法
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
}

public class Client {
    public static void main(String[] args) throws CloneNotSupportedException {
        Author author = new Author("张三");
        Book book1 = new Book("Java设计模式", author);
        
        // 深拷贝
        Book book2 = (Book) book1.clone();
        
        // 修改拷贝对象的引用类型属性
        book2.getAuthor().setName("李四");
        
        // 原对象的引用类型属性没有被修改
        System.out.println("book1的作者: " + book1.getAuthor().getName()); // 张三
        System.out.println("book2的作者: " + book2.getAuthor().getName()); // 李四
    }
}

使用序列化实现深拷贝

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

public class DeepCopyUtil {
    @SuppressWarnings("unchecked")
    public static <T extends Serializable> T deepCopy(T object) {
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(object);
            oos.close();
            
            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bais);
            T deepCopy = (T) ois.readObject();
            ois.close();
            
            return deepCopy;
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
            return null;
        }
    }
}

public class Book implements Serializable {
    private String title;
    private Author author;
    
    public Book(String title, Author author) {
        this.title = title;
        this.author = author;
    }
    
    // Getter和Setter方法
    public String getTitle() {
        return title;
    }
    
    public void setTitle(String title) {
        this.title = title;
    }
    
    public Author getAuthor() {
        return author;
    }
    
    public void setAuthor(Author author) {
        this.author = author;
    }
}

public class Author implements Serializable {
    private String name;
    
    public Author(String name) {
        this.name = name;
    }
    
    // Getter和Setter方法
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
}

public class Client {
    public static void main(String[] args) {
        Author author = new Author("张三");
        Book book1 = new Book("Java设计模式", author);
        
        // 使用序列化实现深拷贝
        Book book2 = DeepCopyUtil.deepCopy(book1);
        
        // 修改拷贝对象的引用类型属性
        book2.getAuthor().setName("李四");
        
        // 原对象的引用类型属性没有被修改
        System.out.println("book1的作者: " + book1.getAuthor().getName()); // 张三
        System.out.println("book2的作者: " + book2.getAuthor().getName()); // 李四
    }
}

优缺点

优点

  1. 性能提高,避免了new操作带来的开销
  2. 简化了对象的创建过程,特别是当创建对象过程比较复杂时
  3. 可以在运行时动态地添加和删除产品
  4. 可以在运行时改变产品的值或结构
  5. 减少了子类的构造,原型模式使用克隆方法生成实例,比使用new创建实例更加灵活

缺点

  1. 配合克隆方法需要考虑深拷贝和浅拷贝的问题
  2. 每个类必须配备一个克隆方法,这对全新的类来说不是很难,但对已有的类不一定很容易
  3. 必须实现Cloneable接口

相关模式

  • 抽象工厂模式:抽象工厂模式可以用原型模式来存储和克隆产品
  • 建造者模式:建造者模式关注的是复杂对象的构建过程,而原型模式关注的是对象的复制
  • 单例模式:单例模式不能与原型模式一起使用,因为单例类不允许被克隆

实际应用

  • Java中的Object.clone()方法
  • Spring框架中的BeanUtils.copyProperties()
  • 各种游戏中的角色复制
  • 文档编辑器中的复制粘贴功能
  • 数据库中的记录复制

注意事项

  1. 使用原型模式时,需要注意深拷贝和浅拷贝的区别
  2. 如果对象中包含不可序列化的对象,则不能使用序列化实现深拷贝
  3. 克隆方法不应该执行构造方法,所以构造方法中的初始化代码不会被执行
  4. 在多线程环境下使用原型模式需要注意线程安全问题
相关推荐
想吃火锅100512 天前
【前端手撕】instanceof
前端·javascript·原型模式
UXbot12 天前
帮助企业低门槛开展AI应用开发的平台推荐
前端·低代码·ui·交互·产品经理·原型模式·web app
UXbot13 天前
如何选择适合公司项目的UI设计工具?企业选型指南
前端·低代码·ui·团队开发·原型模式·设计规范·web app
UXbot13 天前
原型设计工具如何帮助新人快速进入产品行业?
前端·低代码·ui·交互·团队开发·原型模式·web app
sunny.day17 天前
js原型与原型链
开发语言·javascript·原型模式·js原型链
UXbot18 天前
AI网页开发工具能替代工具吗?5大平台对比
前端·人工智能·低代码·ui·原型模式·web app
weixin_3077791318 天前
从“大海捞针”到“主动推理”:AI如何重塑云原生故障诊断的根因链
开发语言·人工智能·算法·自动化·原型模式
swordbob18 天前
prototype 注入到 singleton 里,prototype是否还是线程安全的
安全·spring·单例模式·原型模式
isNotNullX19 天前
企业数据中台建设,ETL工具选错了会踩哪些坑?
数据仓库·etl·原型模式
半个烧饼不加肉19 天前
JS 底层探究-- 普通函数和构造函数
开发语言·javascript·原型模式