【设计模式-原型】

news2024/11/28 22:31:26

**原型模式(Prototype Pattern)**是一种创建型设计模式,旨在通过复制现有对象的方式来创建新对象,而不是通过实例化类来创建对象。该模式允许对象通过克隆(复制)来创建新的实例,因此避免了重新创建对象带来的开销,尤其在对象创建复杂、耗时的情况下。

1. 定义

原型模式使用一个现有的对象作为原型,创建新对象时,通过克隆该对象来生成新对象。这个模式提供了一个接口来复制已有对象,并允许客户端通过该接口请求复制对象。

2. 原型模式的结构

原型模式主要包含以下几个部分:

  • Prototype(原型接口):定义了一个 clone() 方法,用于克隆自身。具体实现可以是浅拷贝或深拷贝。
  • ConcretePrototype(具体原型类):实现了 Prototype 接口,并实现 clone() 方法。通常使用 Java 的 Cloneable 接口来实现克隆。
  • Client(客户端):通过调用原型的 clone() 方法来获取对象,而不是直接通过构造函数创建对象。

3. 原型模式的工作原理

原型模式的关键在于提供一个能够复制自身的原型接口,并在具体类中实现该接口的 clone() 方法。通过克隆现有对象来创建新对象,这样既节省了资源,又能提高系统性能。

3.1 浅拷贝 vs 深拷贝
  • 浅拷贝:复制对象时,拷贝所有字段,但如果字段是对象类型,则只复制对象的引用。换句话说,浅拷贝只是复制了对象的内存地址,因此新旧对象共享相同的引用类型字段。
  • 深拷贝:不仅复制字段,还会递归地复制引用类型的对象,确保新对象是独立的,彼此不共享任何状态。

4. 原型模式的 UML 类图

在这里插入图片描述

5. Java 中的原型模式示例

Java 提供了 Cloneable 接口和 Object 类中的 clone() 方法,支持原型模式的实现。

5.1 示例代码
class Prototype implements Cloneable {
    private String name;
    private int age;
    
    public Prototype(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // 实现 clone 方法
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();  // 浅拷贝
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    @Override
    public String toString() {
        return "Prototype{name='" + name + "', age=" + age + "}";
    }
}

public class PrototypePatternDemo {
    public static void main(String[] args) {
        try {
            // 创建原型对象
            Prototype original = new Prototype("Alice", 25);
            System.out.println("Original Object: " + original);

            // 克隆原型对象
            Prototype clone = (Prototype) original.clone();
            System.out.println("Cloned Object: " + clone);

            // 修改原始对象,会影响克隆对象
            System.out.println("Objects are different: " + (original != clone));
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
}

输出:

Original Object: Prototype{name='Alice', age=25}
Cloned Object: Prototype{name='Alice', age=25}
Objects are different: true
5.2 深拷贝示例
class Address implements Cloneable {
    private String city;
    private String country;

    public Address(String city, String country) {
        this.city = city;
        this.country = country;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();  // 浅拷贝
    }

    @Override
    public String toString() {
        return "Address{city='" + city + "', country='" + country + "'}";
    }
}

class Person implements Cloneable {
    private String name;
    private int age;
    private Address address;

    public Person(String name, int age, Address address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        // 深拷贝: 克隆引用类型字段
        Person cloned = (Person) super.clone();
        cloned.address = (Address) address.clone();  // 深拷贝 Address 对象
        return cloned;
    }

    @Override
    public String toString() {
        return "Person{name='" + name + "', age=" + age + ", address=" + address + "}";
    }
}

public class DeepCopyDemo {
    public static void main(String[] args) {
        try {
            // 创建原型对象
            Address address = new Address("New York", "USA");
            Person person1 = new Person("John", 30, address);
            System.out.println("Original: " + person1);

            // 克隆对象
            Person person2 = (Person) person1.clone();
            System.out.println("Cloned: " + person2);

            // 修改原始对象中的地址,不影响克隆对象中的地址
            address.city = "Los Angeles";
            System.out.println("After modifying address:");
            System.out.println("Original: " + person1);
            System.out.println("Cloned: " + person2);
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
}

输出:

Original: Person{name='John', age=30, address=Address{city='New York', country='USA'}}
Cloned: Person{name='John', age=30, address=Address{city='New York', country='USA'}}
After modifying address:
Original: Person{name='John', age=30, address=Address{city='Los Angeles', country='USA'}}
Cloned: Person{name='John', age=30, address=Address{city='New York', country='USA'}}

6. 原型模式的优点

  • 性能优化:当创建对象代价较高时,克隆可以比通过构造函数创建新对象更加高效。
  • 动态创建对象:原型模式允许在运行时动态地创建对象,而不需要知道具体的类或实现。
  • 减少子类数目:避免了频繁的使用子类去生成对象,通过克隆可以生成对象的不同版本。

7. 原型模式的缺点

  • 深拷贝复杂:如果对象中有复杂的嵌套对象或循环引用,深拷贝的实现会非常复杂,需要逐层克隆每个引用的对象。
  • 依赖于具体的实现:克隆对象需要依赖类的 clone() 方法,可能会限制类的灵活性。

8. 原型模式的应用场景

  • 资源消耗大的对象创建:比如对象创建需要读取数据库、大量计算或需要访问网络资源时,可以使用原型模式进行克隆操作。
  • 重复创建相似对象:在某些系统中,需要频繁创建内容相似的对象,使用原型模式可以节省时间。
  • 不可变对象的场景:某些情况下,创建后不可修改的对象,原型模式可以保证对象的唯一性和一致性。

9. 总结

原型模式是一种有效的设计模式,适用于创建开销较大的对象。通过 clone() 方法,可以快速创建相同或类似的对象,减少创建新对象的成本。同时,它支持浅拷贝和深拷贝的灵活实现,在复杂的对象克隆场景下,深拷贝需要特别的设计。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2222287.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LabVIEW提高开发效率技巧----插入式架构

随着LabVIEW项目规模的扩大和系统复杂性的增加,传统的单一代码架构难以应对后期维护和功能扩展的需求。插入式架构(Plug-In Architecture)作为一种模块化设计方式,通过动态加载和运行子VI,使系统功能更加灵活、模块化&…

Oracle OCP认证考试考点详解082系列01

题记: 本篇博文是Oracle OCP认证考试考点详解082系列的第一篇,本系列主要讲解Oracle OCP认证考试考点(题目),适用于19C/21C,跟着学OCP考试必过。 1. 第一题: 1. 题目 2. 解析及答案 关于Oracle数据库中节…

6.1 特征值介绍

一、特征值和特征向量介绍 本章会开启线性代数的新内容。前面的第一部分是关于 A x b A\boldsymbol x\boldsymbol b Axb:平衡、均衡和稳定状态;现在的第二部分是关于变化的。时间会加入进来 —— 连续时间的微分方程 d u / d t A u \pmb{\textrm{d}…

通过AWS Bedrock探索 Claude 的虚拟桌面魔力:让 AI 代替你动手完成任务!

前言 大家好,昨夜Anthropic 发布了更新。现在 Claude 3.5 Sonnet(V2) 和 Claude 3.5 Haiku,以及名为 computer use 的新功能已经作为公开测试版发布了。 Introducing computer use, a new Claude 3.5 Sonnet, and Claude 3.5 Ha…

生成对抗网络基本原理

生成对抗网络(Generative Adversarial Networks,简称GANs)是由Ian Goodfellow等人在2014年提出的一种深度学习模型,它在生成模型领域引起了革命性的变化。GANs的核心思想是通过引入一个对抗过程来生成新的数据样本,这个…

【1024程序员节】MybatisPlus入门(一)MybatisPlus简介

一、MyBatis简介 MyBatisPlus(简称MP)是基于MyBatis框架基础上开发的增强型工具,旨在简化开发、提高效率 - 官网:https://mybatis.plus/ https://mp.baomidou.com/ MyBatisPlus特性: - 无侵入:只做增强…

接口测试 —— Postman 变量了解一下!

Postman变量是在Postman工具中使用的一种特殊功能,用于存储和管理动态数据。它们可以用于在请求的不同部分、环境或集合之间共享和重复使用值。 Postman变量有以下几种类型: 1、环境变量(Environment Variables): 环境变量是在…

C语言程序设计:现代设计方法习题笔记《chapter3》

第一题 ​ 代码示例&#xff1a; #include<stdio.h>int main() {printf("Enter a date&#xff08;mm/dd/yyyy&#xff09;: ");int day, month, year;scanf_s("%d/%d/%d", &month, &day, &year);printf("%04d%02d%02d", yea…

stm32F103 实现呼吸灯效果

目录 硬件连接 软件实现步骤 初始化系统时钟。 配置 GPIO 引脚。 配置定时器以生成 PWM 信号。 在主循环中调整 PWM 占空比以实现呼吸效果。 示例代码 1. 初始化系统时钟 2. 配置 GPIO 引脚 3. 配置定时器以生成 PWM 信号 4. 在主循环中调整 PWM 占空比以实现呼吸效…

【读书笔记-《网络是怎样连接的》- 2】Chapter2_1-协议栈通信详细过程

第二章从协议栈这部分来看网络中的通信如何实现&#xff0c;准备从两部分来进行分解。本篇是第一部分&#xff1a;详细介绍TCP协议栈收发数据的过程。 首先来看下面的图。从应用程序到网卡需要经过如下几部分&#xff0c;上面的部分通过委托下面的部分来完成工作。首先是应用程…

URP学习(一)

URP是unity出的比较简单的可供改造引擎渲染管线的流程。能实现用较低的代价消耗实现较好的效果。 现记录学习&#xff1a; 一.如何设置URP关键 这步结束后材质会被替换 加package Create/Rendering/URP Universal Rendering Setting设置为urp 材质也需要urp目录下的 几种…

第23周Java主流框架入门-SpringMVC 3.拦截器

Spring MVC 拦截器 (Interceptor) 课程笔记 1. 什么是拦截器 (Interceptor) 拦截器 (Interceptor) 类似于我们之前学习过的 J2EE 过滤器 (Filter)。作用&#xff1a;对请求进行前置和后置的过滤处理。与 Filter 的区别&#xff1a; Interceptor 是 Spring MVC 的标准组件&…

mapbox没有token/token失效,地图闪烁后变空白,报错Error: A valid Mapbox access token is required to use Mapbox GL JS.

目录 mapbox没有token/token失效&#xff0c;地图闪烁后空白&#xff0c;报错Error: A valid Mapbox access token is required to use Mapbox GL JS. 一、问题描述 二、mapbox去除token验证 1、找到mapbox-gl文件夹 2、找到mapbox-gl.js文件 3、找到对应位置并修改 4、清…

14_挂载子节点和元素的属性

目录 挂载子节点简单设置元素的属性区分 HTML Attributes 与 DOM Properties正确的设置元素属性class 的处理 挂载子节点 之前我们描述的 vnode 的 children 只是一个文本&#xff0c;children 是可以为一个数组的&#xff0c;而且大多数情况下&#xff0c;都是一个数组&#…

arm架构 ubuntu 部署docker

如果有旧版本需要卸载 sudo apt remove docker docker-engine docker-ce docker.io 安装依赖包 sudo apt update && apt install -y apt-transport-https ca-certificates curl software-properties-common 添加docker秘钥 阿里云 curl -fsSL http://mirrors.aliyu…

Java应用程序的测试覆盖率之设计与实现(二)-- jacoco agent

说在前面的话 要想获得测试覆盖率报告&#xff0c;第一步要做的是&#xff0c;采集覆盖率数据&#xff0c;并输入到tcp。 而本文便是介绍一种java应用程序部署下的推荐方式。 作为一种通用方案&#xff0c;首先不想对应用程序有所侵入&#xff0c;其次运维和管理方便。 正好…

OAK相机的RGB-D彩色相机去畸变做对齐

▌低畸变标准镜头的OAK相机RGB-D对齐的方法 OAK相机内置的RGB-D管道会自动将深度图和RGB图对齐。其思想是将深度图像中的每个像素与彩色图像中对应的相应像素对齐。产生的RGB-D图像可以用于OAK内置的图像识别模型将识别到的2D物体自动映射到三维空间中去&#xff0c;或者产生的…

openpnp - 底部相机视觉识别CvPipeLine的参数bug修正

文章目录 openpnp - 底部相机视觉识别的CvPipeLine的参数bug概述笔记openpnp的视觉识别参数的错误原因备注补充 - 如果要直接改默认的底部视觉要注意END openpnp - 底部相机视觉识别的CvPipeLine的参数bug 概述 底部相机抓起一个SOD323的元件&#xff0c;进行视觉识别。 识别…

实验:使用Oxygen发布大型手册到Word格式

此前&#xff0c;我曾发表过一篇文章《结构化文档发布的故事和性能调优》&#xff0c;文中讨论了在将大型DITA手册转换为PDF格式时可能遇到的性能挑战及相应的优化策略。 近日&#xff0c;有朋友咨询&#xff0c;若将同样的大型手册输出为MS Word格式&#xff0c;是否也会面临…

【华为HCIP实战课程十八】OSPF的外部路由类型,网络工程师

一、外部路由类型: 上节讲的外部路由类型,无关乎COST大小,OSPF外部路由类型1优先于外部路由类型2 二、转发地址实验拓扑 我们再SW3/R5/R6三台设备运行RIP,SW3即运行RIP又运行OSPF SW3配置rip [SW3-rip-1]ver 2 [SW3-rip-1]network 10.0.0.0 AR5去掉ospf配置和AR6配置rip…