当前位置: 首页 > article >正文

全网最简单的Java设计模式【九】原型模式深入解析

如果觉得本文能够帮到您,请关注🌟、点赞👍、收藏📚,让这份美好延续下去!

一、引言

在 Java 软件开发中,设计模式起着至关重要的作用,它们为解决各种常见的软件设计问题提供了经过验证的方案。其中,原型模式是一种创建型设计模式,它允许通过复制现有对象来创建新对象,而无需了解对象的具体创建过程。这种模式在很多场景下都非常有用,尤其是当对象的创建过程较为复杂或者创建成本较高时。本文将深入探讨 Java 设计模式中的原型模式,包括其定义、实现方式、优缺点以及应用场景,并通过具体的代码示例进行详细说明。

二、原型模式的定义

原型模式(Prototype Pattern)是一种创建对象的方式,通过复制一个已经存在的实例来创建新的实例,而不是通过传统的构造函数来创建。这样可以避免一些复杂的对象初始化过程,提高对象创建的效率。在原型模式中,被复制的对象称为原型对象,新创建的对象称为克隆对象。

三、原型模式的实现方式

在 Java 中,原型模式可以通过两种方式实现:浅克隆(Shallow Clone)和深克隆(Deep Clone)。

(一)浅克隆实现

1. 假设我们有一个包含 基本数据类型引用类型的类

package com.jsglxx.design.prototype;

import java.util.List;

class Person implements Cloneable {
    private int age;
    private List<String> hobbies;

    public Person(int age, List<String> hobbies) {
        this.age = age;
        this.hobbies = hobbies;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public List<String> getHobbies() {
        return hobbies;
    }

    public void setHobbies(List<String> hobbies) {
        this.hobbies = hobbies;
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

2. 使用示例:

package com.jsglxx.design.prototype;

import java.util.ArrayList;
import java.util.List;

public class ShallowCloneExample {
    public static void main(String[] args) {
        try {
            List<String> hobbies = new ArrayList<>();
            hobbies.add("Reading");
            Person original = new Person(30, hobbies);
            Person clone = (Person) original.clone();

            System.out.println("Original: Age = " + original.getAge() + ", Hobbies = " + original.getHobbies());
            System.out.println("Clone: Age = " + clone.getAge() + ", Hobbies = " + clone.getHobbies());

            // 修改原始对象的引用类型字段内容
            original.getHobbies().add("Drawing");
            original.setAge(35);

            System.out.println("After modification:");
            System.out.println("Original: Age = " + original.getAge() + ", Hobbies = " + original.getHobbies());
            System.out.println("Clone: Age = " + clone.getAge() + ", Hobbies = " + clone.getHobbies());
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
}

3. 输出结果:

Original: Age = 30, Hobbies = [Reading]
Clone: Age = 30, Hobbies = [Reading]
After modification:
Original: Age = 35, Hobbies = [Reading, Drawing]
Clone: Age = 30, Hobbies = [Reading, Drawing]

在这个浅克隆示例中:

  • 首先创建了一个原始对象 original,其中包含一个基本数据类型 age 和一个引用类型 hobbies 列表。当通过 super.clone() 进行浅克隆创建 clone 对象时,基本数据类型 age 被按值复制,所以原始对象和克隆对象的 age 是独立的。
  • 然而,对于引用类型 hobbies 列表,浅克隆只是复制了引用,这意味着原始对象和克隆对象中的 hobbies 指向同一个列表对象。
  • 当修改原始对象的 hobbies 列表内容(添加 “Drawing”)以及修改 age 为 35 时,由于克隆对象的 hobbies 引用与原始对象相同,所以克隆对象的 hobbies 列表也会显示添加后的内容。而克隆对象的 age 虽然在克隆时与原始对象相同,但由于基本数据类型是按值复制,所以不会随原始对象的修改而改变。

(二)深克隆示例

1. 为了实现深克隆,可以使用序列化的方式:

package com.jsglxx.design.prototype.deep;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.List;

class Person implements Serializable {

	private static final long serialVersionUID = 1L;
	
	private int age;
    private List<String> hobbies;

    public Person(int age, List<String> hobbies) {
        this.age = age;
        this.hobbies = hobbies;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public List<String> getHobbies() {
        return hobbies;
    }

    public void setHobbies(List<String> hobbies) {
        this.hobbies = hobbies;
    }

    public Person deepClone() {
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(this);

            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bais);
            return (Person) ois.readObject();
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
            return null;
        }
    }
}

2. 使用示例:

package com.jsglxx.design.prototype.deep;

import java.util.ArrayList;
import java.util.List;

public class DeepCloneExample {
    public static void main(String[] args) {
        try {
            List<String> hobbies = new ArrayList<>();
            hobbies.add("Reading");
            Person original = new Person(30, hobbies);
            Person clone = original.deepClone();

            System.out.println("Original: Age = " + original.getAge() + ", Hobbies = " + original.getHobbies());
            System.out.println("Clone: Age = " + clone.getAge() + ", Hobbies = " + clone.getHobbies());

            // 修改原始对象的引用类型字段内容
            original.getHobbies().add("Drawing");
            original.setAge(35);

            System.out.println("After modification:");
            System.out.println("Original: Age = " + original.getAge() + ", Hobbies = " + original.getHobbies());
            System.out.println("Clone: Age = " + clone.getAge() + ", Hobbies = " + clone.getHobbies());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

在这个深克隆示例中,

  1. 通过序列化的方式进行深克隆。首先将原始对象序列化为字节流,然后再从字节流中反序列化出一个新的对象作为克隆对象。
  2. 对于基本数据类型 age,其行为与浅克隆类似,被按值复制,所以原始对象和克隆对象的 age 是独立的。
  3. 对于引用类型 hobbies 列表,在深克隆过程中,实际上创建了一个新的列表对象,并将原始列表中的内容复制到新列表中。这意味着原始对象和克隆对象的 hobbies 列表是完全独立的两个对象。
  4. 当修改原始对象的 hobbies 列表内容(添加 “Drawing”)以及修改 age 为 35 时,由于克隆对象的 hobbies 列表是独立的新对象,所以不会受到原始对象修改的影响。而克隆对象的 age 也同样不会随原始对象的修改而改变。

四、原型模式的优缺点

(一)优点

  1. 性能优化:原型模式通过复制现有对象来创建新对象,避免了复杂的对象初始化过程,从而提高了对象创建的效率。特别是对于创建成本较高的对象,如需要大量计算或占用大量资源的对象,原型模式可以显著提高性能。
  2. 简化对象创建过程:在某些情况下,对象的创建过程可能非常复杂,需要多个步骤和大量的参数。使用原型模式可以将这些复杂的创建过程封装在原型对象中,通过复制原型对象来创建新对象,从而简化了对象创建的过程。
  3. 提供灵活性:原型模式允许在运行时动态地创建对象,并且可以根据需要修改原型对象,从而创建出不同的克隆对象。这种灵活性使得原型模式在一些需要动态创建对象的场景下非常有用,如用户界面开发、游戏开发等。

(二)缺点

  1. 违背开闭原则:为了实现原型模式,需要为每个类都提供一个clone()方法或者实现Serializable接口,这可能会导致代码的可维护性降低。特别是当需要修改类的结构时,可能需要同时修改clone()方法或者序列化过程,这违背了开闭原则。
  2. 深克隆实现复杂:深克隆需要复制对象及其引用的所有对象,这可能会导致实现复杂。特别是对于复杂的对象结构,深克隆可能需要递归地复制所有的引用对象,这可能会导致性能问题和代码的复杂性增加。
  3. 处理循环引用困难:如果对象之间存在循环引用,即一个对象引用了另一个对象,而另一个对象又引用了第一个对象,那么在进行深克隆时可能会出现问题。处理循环引用需要特殊的算法和技术,这可能会增加代码的复杂性。

五、原型模式的应用场景

(一)资源密集型对象创建

当创建对象的成本较高时,如需要大量计算、占用大量内存或需要访问外部资源等,可以使用原型模式来提高性能。通过复制已经存在的对象,可以避免重复的计算和资源访问,从而提高创建对象的效率。

例如,在图形绘制软件中,创建一个复杂的图形对象可能需要大量的计算和内存。如果每次需要绘制这个图形时都重新创建一个对象,将会非常耗时和耗资源。使用原型模式,可以将已经创建好的图形对象作为原型,通过复制原型来创建新的图形对象,从而提高绘制效率。

(二)复杂对象复制

当需要复制一个复杂的对象结构时,使用原型模式可以简化复制过程。通过复制原型对象,可以避免手动复制每个子对象的繁琐过程,并且可以确保复制的准确性。

例如,在一个游戏开发中,角色对象可能包含多个属性和子对象,如位置、速度、装备等。如果需要复制一个角色对象,可以使用原型模式来复制整个角色对象及其所有的子对象,从而确保复制的准确性和完整性。

(三)动态对象创建

在一些需要动态创建对象的场景下,如用户界面开发、游戏开发等,可以使用原型模式来提供灵活性。通过修改原型对象,可以创建出不同的克隆对象,从而满足不同的需求。

例如,在一个用户界面开发中,可能需要根据用户的选择动态地创建不同类型的窗口或控件。使用原型模式,可以将不同类型的窗口或控件作为原型,通过复制原型来创建新的窗口或控件,从而满足用户的需求。

六、总结

原型模式是一种非常有用的设计模式,它允许通过复制现有对象来创建新对象,从而提高对象创建的效率和灵活性。在 Java 中,可以通过浅克隆和深克隆两种方式实现原型模式。浅克隆只复制对象的基本数据类型字段和引用类型字段的引用,而深克隆则会复制对象及其引用的所有对象。原型模式具有性能优化、简化对象创建过程和提供灵活性等优点,但也存在违背开闭原则、深克隆实现复杂和处理循环引用困难等缺点。在实际应用中,需要根据具体情况选择合适的实现方式,并注意处理可能出现的问题。

🌟 对技术管理感兴趣 请关注下方 ⬇ 【 技术管理修行】


http://www.kler.cn/a/369103.html

相关文章:

  • 10.28Python_数据结构_栈和链表
  • 【CSS3】css开篇基础(4)
  • linux指令笔记
  • 【QT】Qt窗口(上)
  • 图---java---黑马
  • MySql中表的复合查询
  • 深入分析梧桐数据库SQL查询之挖掘季度销售冠军
  • 安卓14上蓝牙调用SystemProperties.set(),解决找不到SystemProperties.set()的问题
  • Ubuntu 2张4090,显卡安装,无法双屏显示
  • [产品管理-51]:产品经理:塑造未来的多面手,道、法、术、器的全面掌控
  • 【完整版】opencv-python-headless、opencv-python和opencv-contrib-python区别和联系
  • AI Weekly3:过去一周重要的AI资讯汇总
  • 记录一个docker volume映射目录创建文件报错问题
  • 量子容错计算
  • ts:数组的常用方法(filter)
  • 开源模型应用落地-Qwen2.5-7B-Instruct与vllm实现离线推理-使用Lora权重(三)
  • python包的其他安装方法:whl、.tar.gz
  • 2024 年 MathorCup 数学应用挑战赛——大数据竞赛-赛道 A:台风的分类与预测
  • 【Docker大揭秘】
  • 【力扣】[Java版] 刷题笔记-70. 爬楼梯
  • JavaScript 前端开发
  • Python 网络爬虫:基础与实践
  • Java并发学习总结:原子操作类
  • python:如何判断一个数是否为素数
  • Go语言初识
  • 基于Python和OpenCV的疲劳检测系统设计与实现