二十三种设计模式-原型模式

原型模式(Prototype Pattern)是一种创建型设计模式,它通过拷贝现有的实例来创建新的实例,而不是通过新建实例。这种方式可以避免复杂的构造过程,同时还能保持对象的创建和使用分离,提高系统的灵活性和扩展性。

了解原型模式之前需要先搞懂浅拷贝和深拷贝。

浅拷贝指的是创建一个新对象,然后将原对象的非静态字段复制到新对象中。如果字段是基本数据类型,那么直接复制其值;如果字段是对象引用,则复制引用本身,而不是引用的对象。这意味着新对象和原对象的引用字段指向同一个对象。

深拷贝意味着不仅要复制对象本身,还要复制对象中所有引用的对象,以及这些引用对象中引用的其他对象,直到所有相关对象都被复制。

另外java中有一些特殊的引用类型(不可变对象),如下。这些引用类型创建的对象是不可变对象,在拷贝是都是理解成是一种深拷贝。

在Java中,不可变对象是指一旦创建后,其状态(即对象的字段值)就不能被改变的对象。不可变对象具有线程安全性、可缓存性和可共享性等优点。以下是一些常见的不可变引用类型对象:

基本数据类型的包装类
Integer
Byte
Short
Long
Float
Double
Character
Boolean
这些包装类都是基本数据类型的对应引用类型。它们是不可变的,因为它们的值一旦被设置,就不能被改变。例如,当你对一个Integer对象进行加法运算时,实际上会创建一个新的Integer对象来表示新的值,而不是修改原来的对象。

字符串类
String
String类是Java中最常用的不可变类之一。字符串对象一旦被创建,其内容就不能被改变。例如,当你使用+操作符连接两个字符串时,会创建一个新的字符串对象,而不是修改原来的字符串对象。

原子类
AtomicInteger
AtomicLong
AtomicBoolean
其他java.util.concurrent.atomic包中的原子类
这些原子类提供了对基本数据类型的安全操作,它们是不可变的,因为它们的值只能通过原子操作来更新,而不是直接修改对象的状态。

其他不可变类
BigDecimal 和 BigInteger
这些类用于表示任意精度的数值。它们是不可变的,因为每次进行数学运算时,都会返回一个新的对象来表示结果,而不是修改原来的对象。

java.time包中的日期和时间类
例如:

LocalDate
LocalTime
LocalDateTime
ZonedDateTime
Duration
Period
这些类是Java 8引入的新的日期和时间API的一部分。它们都是不可变的,每次进行日期或时间的修改操作时,都会返回一个新的对象。

不可变对象的设计原则
要设计一个不可变对象,通常需要遵循以下原则:

所有字段都是final的:确保字段一旦被初始化,就不能被改变.
不提供修改字段值的方法:不提供任何可以修改对象状态的方法.
确保对象的所有字段都是不可变的:如果对象的字段是引用类型,确保这些引用类型也是不可变的.
如果需要提供访问字段的方法,返回字段的副本而不是引用:例如,对于包含可变对象的字段,返回字段的副本,以防止外部代码修改对象的状态.
通过设计不可变对象,可以提高程序的安全性和可维护性,特别是在多线程环境中.

原型模式核心概念

  • 原型(Prototype) :声明一个克隆自身的接口,用于创建当前对象的副本。在Java中,通常通过实现Cloneable接口并重写clone()方法来实现原型模式。
  • 具体原型(ConcretePrototype):实现原型接口,提供具体的克隆实现,生成自己的副本。

实现步骤

  1. 实现Cloneable接口 :让类实现Cloneable接口,表示该类的对象可以被克隆。
  2. 重写clone()方法 :在类中重写Object类的clone()方法,以实现深拷贝或浅拷贝。
  3. 创建原型对象:创建一个原型对象,作为后续克隆的模板。
  4. 克隆原型对象 :通过调用原型对象的clone()方法,创建新的对象实例.

示例代码

以下是一个简单的Java示例,展示如何使用原型模式:

import java.util.Date;

// 具体原型类
class Resume implements Cloneable {
    private String name;
    private String sex;
    private String age;
    private Date birthDate; // 假设包含日期对象

    public Resume(String name, String sex, String age) {
        this.name = name;
        this.sex = sex;
        this.age = age;
        this.birthDate = new Date(); // 初始化日期对象
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        // 实现深拷贝
        
        //super.clone()是调用Object类的clone()方法,它会创建一个新的对象,并将原对象的字段值复制到新对象中。对于基本数据类型的字段,值会被直接复制;而对于引用类型的字段,复制的是引用本身,而不是引用所指向的对象. 这一步本身是浅拷贝.
        Resume clonedResume = (Resume) super.clone();

        clonedResume.birthDate = (Date) this.birthDate.clone();
        return clonedResume;
    }

    @Override
    public String toString() {
        return "Resume{" +
                "name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                ", age='" + age + '\'' +
                ", birthDate=" + birthDate +
                '}';
    }
}

public class PrototypePatternDemo {
    public static void main(String[] args) throws CloneNotSupportedException {
        Resume originalResume = new Resume("John Doe", "Male", "30");
        System.out.println("Original Resume: " + originalResume);

        Resume clonedResume = (Resume) originalResume.clone();
        System.out.println("Cloned Resume: " + clonedResume);
    }
}

优缺点

  • 优点
    • 性能优势:通过拷贝现有对象,避免了复杂的构造过程,特别是在对象创建过程中需要进行大量数据计算或资源获取时.
    • 代码简洁:简化了对象的创建代码,使得代码更加简洁和易于维护.
  • 缺点
    • 实现复杂:需要实现深拷贝,对于包含复杂对象引用的类,实现起来可能比较复杂.
    • 对类的约束:需要实现Cloneable接口,并且所有涉及的对象都需要支持拷贝操作,这可能对类的设计有一定的约束.

适用场景

  • 当一个系统应该独立于其产品的创建、组合和表示时.
  • 当需要通过动态指定创建对象的类别、数目和初始化参数时.
  • 当对象的创建过程复杂,且需要避免重复创建大量相似对象时.
相关推荐
難釋懷2 小时前
命令模式详解与应用
设计模式·命令模式
中國移动丶移不动5 小时前
深入解读五种常见 Java 设计模式及其在 Spring 框架中的应用
java·后端·spring·设计模式·mybatis
silver6879 小时前
状态模式详解
设计模式
JINGWHALE19 小时前
设计模式 行为型 状态模式(State Pattern)与 常见技术框架应用 解析
前端·人工智能·后端·设计模式·性能优化·系统架构·状态模式
游客52011 小时前
设计模式-结构型-桥接模式
开发语言·python·设计模式·桥接模式
苹果13 小时前
C++二十三种设计模式之工厂方法模式
c++·设计模式·工厂方法模式
程序研13 小时前
工厂方法模式
java·设计模式
渊渟岳13 小时前
掌握设计模式--享元模式
设计模式
全栈若城13 小时前
4种革新性AI Agent工作流设计模式全解析
人工智能·设计模式