Java设计模式-原型模式

发布于:2025-08-19 ⋅ 阅读:(12) ⋅ 点赞:(0)

Java设计模式-原型模式

模式概述

原型模式简介

定义:通过复制(克隆)一个已存在的对象(原型)来创建新对象,而不是通过传统的new关键字调用构造方法生成。原型模式通过封装对象的复制逻辑,避免了重复初始化的复杂操作,尤其适用于对象创建成本较高(如涉及数据库查询、IO读取或复杂计算)的场景。

模式类型:创建型模式(Creational Pattern)。

作用

  • 降低对象创建成本:通过克隆已有对象,避免重复执行高开销的初始化逻辑(如数据库查询、资源加载);
  • 简化对象创建流程:将复杂对象的构造逻辑封装在原型类中,客户端只需调用克隆方法即可获得新对象;
  • 支持动态生成对象:通过修改原型对象的属性,可以快速生成不同配置的新对象;
  • 避免继承冗余:相比通过子类扩展对象(如SubClass extends BaseClass),原型模式通过克隆实现更灵活的动态扩展。

典型应用场景

  • 对象创建成本高(如需要从数据库加载配置的User对象、需要初始化大型资源的GameCharacter);
  • 需要动态生成对象(如根据用户输入实时创建不同参数的ImageFilter);
  • 避免使用子类扩展对象(如系统需要支持多种类型的Document,但通过继承扩展会导致类爆炸);
  • 需要保证对象初始化的一致性(如所有Report对象需包含相同的默认标题和页眉,通过克隆原型保证一致性)。

我认为:原型模式就像“对象复印机”——用一个已有的对象当模板,直接“复印”出多个相同或相似的新对象,既省去了重新排版的麻烦(避免重复初始化),又能快速得到结果。

课程目标

  • 理解原型模式的核心思想和经典应用场景
  • 识别应用场景,使用原型模式解决功能要求
  • 了解原型模式的优缺点

核心组件

角色-职责表

角色 职责 示例类名
抽象原型(Prototype) 定义克隆自身的抽象方法(如clone()),通常是接口或抽象类。 Shape(图形接口)
具体原型(ConcretePrototype) 实现抽象原型的clone()方法,完成自身的深拷贝或浅拷贝。 Circle(圆形)、Rectangle(矩形)
客户端(Client) 持有原型对象,通过调用clone()方法创建新对象。 ShapeFactory(图形工厂)

类图

下面是一个简化的类图表示,展示了原型模式中的主要角色及其交互方式:

使用原型克隆对象
«interface»
Prototype
+clone()
Circle
-double radius
-String color
-long createTime
+Circle(double radius, String color)
+clone()
+getRadius()
+setRadius(double radius)
+getColor()
+setColor(String color)
+getCreateTime()
Client
+main(String[] args)

传统实现 VS 原型模式

案例需求

案例背景:设计一个图形绘制系统,需要频繁创建不同颜色和半径的圆形(Circle)。每个圆形对象需要初始化颜色(需从配置文件读取)、半径(用户输入),并记录创建时间(系统时间戳)。传统方式通过new关键字创建对象,但初始化逻辑复杂且重复。

传统实现(痛点版)

代码实现

// 传统方式:通过工厂方法创建对象(高开销)
public class CircleFactory {
    // 模拟高开销操作:从配置文件读取默认颜色(假设需要IO)
    private static String getDefaultColor() {
        try {
            Thread.sleep(100); // 模拟IO延迟
            return "RED"; // 实际从config.properties读取
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    // 模拟高开销操作:获取系统时间戳
    private static long getCurrentTimestamp() {
        try {
            Thread.sleep(50); // 模拟系统调用延迟
            return System.currentTimeMillis();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    // 创建圆形对象(每次需重复执行默认颜色和时间戳获取)
    public static Circle createCircle(double radius) {
        String color = getDefaultColor(); // 高开销(IO)
        long createTime = getCurrentTimestamp(); // 高开销(系统调用)
        return new Circle(radius, color, createTime);
    }
}

// 圆形对象
public class Circle {
    private double radius;
    private String color;
    private long createTime;

    public Circle(double radius, String color, long createTime) {
        this.radius = radius;
        this.color = color;
        this.createTime = createTime;
    }

    // Getter/Setter...
    public double getRadius() { return radius; }
    public void setRadius(double radius) { this.radius = radius; }
    public String getColor() { return color; }
    public void setColor(String color) { this.color = color; }
    public long getCreateTime() { return createTime; }
}

// 客户端使用
public class Client {
    public static void main(String[] args) {
        // 创建第一个圆形(耗时:100ms IO + 50ms 系统调用)
        Circle circle1 = CircleFactory.createCircle(10.0);
        // 创建第二个圆形(同样需要重复执行100ms IO + 50ms 系统调用)
        Circle circle2 = CircleFactory.createCircle(20.0);
    }
}

痛点总结

  • 高开销重复操作:每次创建对象都需执行getDefaultColor()(IO)和getCurrentTimestamp()(系统调用),当需要创建大量对象时(如1000次),总耗时会线性增长(100+50)*1000=150,000ms=150秒);
  • 代码冗余:初始化逻辑(如读取配置、时间戳)封装在工厂方法中,但每次创建仍需重复调用,无法复用已初始化的对象;
  • 灵活性差:若需要修改默认颜色(如从RED改为BLUE),需修改工厂方法的getDefaultColor()逻辑,并影响所有新创建的对象。

原型模式 实现(优雅版)

代码实现

// 1. 抽象原型:定义克隆方法
public interface Prototype {
    Prototype clone(); // 浅拷贝或深拷贝
}

// 2. 具体原型:圆形(实现克隆逻辑)
public class Circle implements Prototype {
    private double radius;
    private String color;
    private long createTime;

    // 构造方法(仅初始化一次,高开销操作在此完成)
    public Circle(double radius, String color) {
        this.radius = radius;
        this.color = color;
        try {
            Thread.sleep(100); // 模拟IO读取颜色(仅执行一次)
            this.color = color;
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        this.createTime = System.currentTimeMillis(); // 系统时间戳(仅执行一次)
    }

    // 克隆方法(深拷贝:复制所有属性)
    @Override
    public Circle clone() {
        // 直接创建新对象,复制当前实例的属性(无需重复IO或系统调用)
        Circle cloned = new Circle(this.radius, this.color);
        // 若需要独立修改时间戳,可重置(可选)
        cloned.createTime = System.currentTimeMillis(); 
        return cloned;
    }

    // Getter/Setter...
    public double getRadius() { return radius; }
    public void setRadius(double radius) { this.radius = radius; }
    public String getColor() { return color; }
    public void setColor(String color) { this.color = color; }
    public long getCreateTime() { return createTime; }
}

// 3. 客户端:使用原型克隆对象
public class Client {
    public static void main(String[] args) {
        // 初始化原型对象(仅执行一次高开销操作:IO读取颜色)
        Circle prototype = new Circle(10.0, "RED"); 

        // 克隆原型生成新对象(无需重复IO,仅需复制属性)
        Circle circle1 = prototype.clone(); 
        circle1.setRadius(15.0); // 修改半径

        Circle circle2 = prototype.clone(); 
        circle2.setColor("BLUE"); // 修改颜色

        // 输出结果
        System.out.println("circle1: radius=" + circle1.getRadius() + ", color=" + circle1.getColor() + ", createTime=" + circle1.getCreateTime());
        System.out.println("circle2: radius=" + circle2.getRadius() + ", color=" + circle2.getColor() + ", createTime=" + circle2.getCreateTime());
        System.out.println("prototype: radius=" + prototype.getRadius() + ", color=" + prototype.getColor() + ", createTime=" + prototype.getCreateTime());
    }
}

优势

  • 降低开销:原型对象仅在首次创建时执行高开销操作(如IO读取颜色),后续克隆仅需复制属性,总耗时从150秒降至约100ms(仅首次)+ 2 * 0ms(克隆)≈100ms;
  • 灵活扩展:通过修改原型对象的属性(如prototype.setColor("GREEN")),可快速生成不同配置的新对象;
  • 代码简洁:克隆逻辑封装在原型类中,客户端只需调用clone()方法,无需关心初始化细节;
  • 一致性保证:所有克隆对象的基础属性(如颜色)与原型一致,避免因重复初始化导致的配置错误。

局限

  • 深拷贝复杂度:若对象包含引用类型属性(如List、其他对象),需实现深拷贝(重写clone()方法并递归克隆引用对象),否则可能导致多个克隆对象共享同一引用,引发意外修改;
  • 原型管理成本:若需要管理多个原型(如不同颜色、尺寸的原型),需引入原型管理器(PrototypeManager)来存储和查找原型,增加系统复杂度;
  • 不适合动态变化对象:若对象的状态在运行时频繁变化(如实时更新的传感器数据),克隆可能导致旧状态被保留,需谨慎使用。

模式变体

  • 浅拷贝原型:仅复制基本类型属性和引用类型地址(默认clone()方法行为),适用于引用类型属性不可变(如String)的场景;
  • 深拷贝原型:递归复制所有引用类型属性(如通过序列化/反序列化实现),适用于引用类型属性可变的场景(如List、自定义对象);
  • 不可变原型:原型对象创建后不可修改(属性为final),克隆时直接返回新对象,避免共享状态问题;
  • 缓存原型(原型管理器):通过Map缓存常用原型(如Map<String, Prototype> prototypeCache),客户端通过键名获取原型并克隆,减少重复创建;
  • 懒加载原型:原型对象在首次使用时克隆(而非提前创建),适用于资源敏感的场景(如内存受限的系统)。

最佳实践

建议 理由
优先使用深拷贝 若对象包含可变引用类型属性(如List、其他对象),深拷贝可避免克隆对象与原型共享状态,防止意外修改。
明确克隆语义 在文档中说明克隆是深拷贝还是浅拷贝(如Java的ArrayList.clone()是浅拷贝),避免客户端误解。
原型对象不可变 若原型对象创建后无需修改(如配置模板),将其属性设为final,确保克隆结果的一致性和线程安全。
使用原型管理器 当需要管理多个原型时(如不同类型、配置的原型),通过PrototypeManager统一存储和查找,提高灵活性。
避免过度克隆 若对象创建成本不高(如仅包含基本类型属性),直接使用new关键字更简单,无需引入原型模式。

一句话总结

原型模式通过克隆已有对象高效创建新对象,避免了重复初始化的高开销操作,尤其适用于对象创建复杂或需要动态生成多配置对象的场景。

如果关注Java设计模式内容,可以查阅作者的其他Java设计模式系列文章。😊


网站公告

今日签到

点亮在社区的每一天
去签到