设计模式精讲 Day 5:原型模式(Prototype Pattern)

【设计模式精讲 Day 5】原型模式(Prototype Pattern)


文章内容

在“设计模式精讲”系列的第5天,我们将深入讲解原型模式(Prototype Pattern)。作为创建型设计模式之一,原型模式通过复制已有对象来创建新对象,避免了重复初始化和构造过程,提升了系统性能和灵活性。

原型模式的核心思想是:通过克隆已有的对象实例来创建新的对象,而不是通过构造函数或工厂方法进行实例化。它特别适用于那些构造成本较高、配置复杂的对象场景。在实际开发中,原型模式被广泛应用于对象池、缓存、图形界面组件复制等场景。

本文将从理论到实践全面解析原型模式,包括其定义、结构、适用场景、实现方式、优缺点分析,并结合真实项目案例说明其应用价值。同时,我们还会探讨其与其它设计模式的关系,并展示在Java标准库中的实际应用。


模式定义

原型模式是一种创建型设计模式,它通过复制一个已有对象(即原型)来创建新对象,而不是通过调用构造函数或工厂方法。该模式的核心思想是:通过克隆操作减少对象创建的开销,提高系统效率

原型模式的关键在于提供一个可以被复制的对象接口,通常通过实现 Cloneable 接口并重写 clone() 方法来实现。在 Java 中,Object 类提供了 clone() 方法,但默认是浅拷贝(Shallow Copy),如果需要深拷贝(Deep Copy),则需要手动实现。


模式结构

原型模式的 UML 类图包含以下几个关键角色:

  • Prototype(原型类):声明一个克隆自身的方法(通常是 clone())。
  • ConcretePrototype(具体原型类):实现 clone() 方法,返回自身的一个副本。
  • Client(客户端):使用原型对象来创建新的对象,而不是直接使用构造函数。

文字描述如下:

  1. Prototype 是一个抽象类或接口,定义了 clone() 方法。
  2. ConcretePrototype 是实现了 clone() 方法的具体类,负责生成自身的副本。
  3. Client 调用 clone() 方法来创建新对象,而无需显式地调用构造函数。

适用场景

原型模式适用于以下几种典型场景:

场景描述
构造成本高当对象的构造过程复杂、耗时较长时,使用克隆可以节省资源。
配置复杂对象的初始化需要多个参数或依赖关系,克隆可以避免重复配置。
动态配置需要根据运行时配置动态创建对象,克隆可以快速生成相似对象。
多种变体需要创建多个类似对象,且它们之间只有少量差异时,克隆比重新构造更高效。

例如,在图形编辑器中,用户可以通过拖拽一个图形元素来复制它,此时就可以使用原型模式来实现快速复制功能。


实现方式

下面是一个完整的 Java 示例,演示如何使用原型模式创建对象。

1. 定义原型接口
public interface Prototype extends Cloneable {Prototype clone();
}
2. 实现具体原型类
public class ConcretePrototype implements Prototype {private String name;private int value;public ConcretePrototype(String name, int value) {this.name = name;this.value = value;}// 浅拷贝实现@Overridepublic Prototype clone() {try {return (ConcretePrototype) super.clone();} catch (CloneNotSupportedException e) {throw new RuntimeException("克隆失败", e);}}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getValue() {return value;}public void setValue(int value) {this.value = value;}@Overridepublic String toString() {return "ConcretePrototype{name='" + name + "', value=" + value + "}";}
}
3. 客户端代码示例
public class Client {public static void main(String[] args) {ConcretePrototype prototype = new ConcretePrototype("原型对象", 100);// 克隆对象ConcretePrototype cloned = (ConcretePrototype) prototype.clone();cloned.setName("克隆对象");cloned.setValue(200);System.out.println("原始对象: " + prototype);System.out.println("克隆对象: " + cloned);}
}

输出结果:

原始对象: ConcretePrototype{name='原型对象', value=100}
克隆对象: ConcretePrototype{name='克隆对象', value=200}
4. 深拷贝实现(可选)

如果对象内部有引用类型字段,需要手动实现深拷贝:

public class DeepPrototype implements Prototype {private String name;private int value;private List<String> tags;public DeepPrototype(String name, int value, List<String> tags) {this.name = name;this.value = value;this.tags = new ArrayList<>(tags); // 浅拷贝}@Overridepublic Prototype clone() {DeepPrototype clone = new DeepPrototype(this.name, this.value, new ArrayList<>());clone.tags.addAll(this.tags); // 深拷贝return clone;}// 省略 getter/setter 和 toString 方法
}

工作原理

原型模式的核心机制是对象的复制,而非传统的构造过程。通过调用 clone() 方法,可以快速生成一个与原对象相同状态的新对象。

这种机制的优势在于:

  • 避免重复初始化:不需要每次都重新执行构造逻辑,尤其适用于构造复杂、资源消耗大的对象。
  • 提高性能:在某些场景下,克隆操作比重新构造对象更快。
  • 简化对象创建流程:客户端只需知道一个原型对象即可创建多个相似对象,降低了耦合度。

优缺点分析

优点缺点
提高对象创建效率,避免重复初始化如果对象内部包含复杂引用结构,深拷贝实现较为复杂
减少对构造函数的依赖,降低耦合不适合所有场景,尤其是需要严格控制对象生命周期的情况
易于扩展,支持多种变体对象可能导致对象状态不一致,特别是当原型对象被修改后

案例分析:图形编辑器中的对象复制

假设我们正在开发一个图形编辑器,用户可以绘制矩形、圆形等图形,并能够通过拖拽复制图形。如果每次复制都重新创建图形对象,会浪费大量资源,尤其是在图形数量较多时。

问题:

  • 每次复制都需要重新设置图形的属性(如位置、大小、颜色等)。
  • 重复构造图形对象,增加内存和计算开销。

解决方案:

  • 使用原型模式,让图形类实现 clone() 方法。
  • 用户点击复制按钮时,调用图形对象的 clone() 方法,生成一个新的图形副本。
  • 新图形对象继承原图形的所有属性,仅需调整位置即可。

代码示例:

public abstract class Shape implements Prototype {protected String color;public Shape(String color) {this.color = color;}public abstract void draw();@Overridepublic abstract Shape clone();
}public class Rectangle extends Shape {private int width;private int height;public Rectangle(String color, int width, int height) {super(color);this.width = width;this.height = height;}@Overridepublic void draw() {System.out.println("绘制矩形,颜色:" + color + ", 宽:" + width + ", 高:" + height);}@Overridepublic Rectangle clone() {return new Rectangle(this.color, this.width, this.height);}
}

客户端使用:

public class Editor {public static void main(String[] args) {Shape original = new Rectangle("红色", 100, 50);Shape copy = original.clone();original.draw();  // 输出:绘制矩形,颜色:红色, 宽:100, 高:50copy.draw();      // 输出:绘制矩形,颜色:红色, 宽:100, 高:50}
}

与其他模式的关系

原型模式常与以下模式结合使用:

模式关系说明
工厂模式原型模式可以看作是工厂模式的一种替代方案,特别是在对象构造复杂时。
建造者模式两者都可以用于创建复杂对象,但建造者关注的是分步骤构建,原型关注的是复制已有对象。
单例模式在某些场景下,原型模式可以与单例模式结合使用,确保只有一份原型对象被共享。

此外,原型模式还可以与享元模式结合使用,用于创建共享的、可复用的对象实例。


总结

原型模式是一种高效的创建型设计模式,通过复制已有对象来创建新对象,避免了重复构造和初始化过程。它适用于构造成本高、配置复杂的对象场景,具有良好的灵活性和扩展性。

在本篇文章中,我们详细介绍了原型模式的定义、结构、实现方式、工作原理、优缺点以及实际应用场景。通过 Java 代码示例,我们展示了如何在实际项目中使用原型模式,并讨论了其与其它设计模式的关系。

下一节我们将进入“设计模式精讲”的第6天,讲解适配器模式(Adapter Pattern),它是结构型设计模式中非常重要的一个模式,主要用于解决接口不兼容的问题。


文章标签

design-patterns, java, software-engineering, oop, object-oriented-programming, design-pattern-day5, prototype-pattern


文章简述

在“设计模式精讲”系列的第5天,我们深入讲解了原型模式(Prototype Pattern),这是一种通过复制已有对象来创建新对象的创建型设计模式。文章从理论到实践全面解析了该模式的定义、结构、适用场景、实现方式,并结合真实项目案例说明其应用价值。我们还通过完整的 Java 代码示例展示了原型模式的实现过程,包括浅拷贝与深拷贝的区别,以及如何在图形编辑器等实际场景中使用该模式。最后,我们分析了原型模式与其他设计模式的关系,并总结了其优缺点。通过本文的学习,读者将掌握如何在实际项目中高效地使用原型模式,提升系统的性能和可维护性。


进一步学习资料

  1. Design Patterns: Elements of Reusable Object-Oriented Software
  2. Java Design Patterns - A Hands-On Guide with Examples
  3. Refactoring Guru - Prototype Pattern
  4. Java Documentation - Object.clone()
  5. GeeksforGeeks - Prototype Design Pattern in Java

核心设计思想回顾

  • 原型模式通过复制已有对象来创建新对象,避免重复构造。
  • 适用于构造成本高、配置复杂的对象场景。
  • 在 Java 中可通过实现 Cloneable 接口并重写 clone() 方法实现。
  • 支持浅拷贝与深拷贝两种方式,需根据业务需求选择。
  • 与工厂模式、建造者模式等结合使用,提升系统灵活性和可维护性。

在实际开发中,合理使用原型模式可以显著提升系统性能,特别是在需要频繁创建相似对象的场景中。

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

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

相关文章

深度学习——第2章习题2-1分析为什么平方损失函数不适用于分类问题

深度学习——第2章习题2-1 《神经网络与深度学习》——邱锡鹏 2-1 分析为什么平方损失函数不适用于分类问题。 平方损失函数&#xff08;Quadratic Loss Function&#xff09;经常用在预测标签y为实数值的任务中&#xff0c;定义为 L ( y , f ( x ; θ ) ) 1 2 ( y − f (…

【Linux】运行脚本后打屏同时保存到本地

命令&#xff1a; sh run.sh 2>&1 | tee output.log sh run.sh 2>&1 | tee output_$(date "%Y%m%d%H%M").log作用&#xff1a;运行脚本&#xff0c;并同时将输出&#xff08;包括标准输出和错误输出&#xff09;显示到终端&#xff0c;并保存到文件中…

Spark 在小众日常场景中的实战应用:从小店数据到社区活动

Spark 在小众日常场景中的实战应用&#xff1a;从小店数据到社区活动​ 提起 Spark&#xff0c;大家往往会联想到大型互联网公司的数据处理、金融行业的复杂分析。但实际上&#xff0c;Spark 在许多小众、贴近生活的场景中也能大显身手。结合学习与实践&#xff0c;我探索了 S…

mysql 执行计划 explain命令 详解

explain id &#xff1a;select查询的序列号&#xff0c;包含一组数字&#xff0c;表示查询中执行select子句或操作表的顺序select_type&#xff1a;查询类型 或者是 其他操作类型table &#xff1a;正在访问哪个表partitions &#xff1a;匹配的分区type &#xff1a;访问的类…

让大模型“更懂人话”:对齐训练(RLHF DPO)全流程实战解析

网罗开发 &#xff08;小红书、快手、视频号同名&#xff09; 大家好&#xff0c;我是 展菲&#xff0c;目前在上市企业从事人工智能项目研发管理工作&#xff0c;平时热衷于分享各种编程领域的软硬技能知识以及前沿技术&#xff0c;包括iOS、前端、Harmony OS、Java、Python等…

GO 原子操作面试题及参考答案

Go 的 sync/atomic 包和 sync.Mutex 的根本区别是什么&#xff1f; Go 语言中的 sync/atomic 包和 sync.Mutex 都用于处理并发编程中的同步问题&#xff0c;但它们的实现机制、应用场景和性能特性存在根本差异。理解这些差异对于编写高效、安全的并发代码至关重要。 sync/atomi…

MATLAB 山脊图绘制全解析:从数据生成到可视化进阶

一、引言&#xff1a;当数据分布拥有「层次感」—— 山脊图的魅力​ 在数据可视化的世界里&#xff0c;我们常常需要同时展示多个分布的形态差异。传统的重叠密度图虽然能呈现整体趋势&#xff0c;但当分布数量较多时&#xff0c;曲线交叠会让画面变得杂乱。这时候&#xff0c…

跨境电商每周资讯—6.16-6.20

1. Instagram 在亚太地区逐渐超越 TikTok 在整个亚太地区&#xff0c;Instagram用户数量正逐渐超过TikTok。预计2025年日本Instagram用户数量将增至4440万&#xff0c;印度今年用户数量将增长10%&#xff0c;领跑亚太。与之形成对比的是&#xff0c;TikTok在一些国家增长速度放…

计算机网络 网络层:数据平面(一)

前一节学习了运输层依赖于网络层的主机到主机的通信服务&#xff0c;提供各种形式的进程到进程的通信。了解这种主机到主机通信服务的真实情况&#xff0c;是什么使得它工作起来的。 在本章和下一章&#xff0c;将学习网络层实际是怎样实现主机到主机的通信服务。与运输层和应用…

Suna本地部署详细教程

一、安装基础环境 # 1、创建环境 conda create -n suna python3.11.7# 2、激活虚拟环境 conda activate suna# 3、安装jupyter和ipykernel pip install jupyter ipykernel# 4、将虚拟环境添加到jupyter # python -m ipykernel install --user --namemyenv --display-name"…

LeetCode 每日一题打卡|若谷的刷题日记 3day--最长连续序列

1.最长连续序列 题目&#xff1a; 给定一个未排序的整数数组 nums &#xff0c;找出数字连续的最长序列&#xff08;不要求序列元素在原数组中连续&#xff09;的长度。 请你设计并实现时间复杂度为 O(n) 的算法解决此问题。 示例 1&#xff1a; 输入&#xff1a;nums [1…

EfficientVLA:面向视觉-语言-动作模型无训练的加速与压缩

25年6月来自上海交大、哈工大、西安交大和电子科大&#xff08;成都&#xff09;的论文“EfficientVLA: Training-Free Acceleration and Compression for Vision-Language-Action Models”。 视觉-语言-动作 (VLA) 模型&#xff0c;特别是基于扩散的架构&#xff0c;展现出具…

wireshark抓包分析TCP数据包

1、直接从TCP的三次握手开始说起 三次握手就是客户与服务器建立连接的过程 客户向服务器发送SYN(SEQ=x)报文,然后就会进入SYN_SEND状态服务器收到SYN报文之后,回应一个SYN(SEQ=y)ACK(ACK=x+1)报文,然后就会进入SYN_RECV状态客户收到服务器的SYN报文,回应一个ACK(AC…

同等学力申硕-计算机统考-历年真题和备考经验

同等学力申请硕士学位考试是比较适合在职人员的提升学位方式&#xff0c;了解过的人应该都知道&#xff0c;现在社会的竞争压力越来越大&#xff0c;为了提高职业生存能力&#xff0c;提升学位在所难免。 一、已有计算机统考历年真题资料 报名过同等学力申硕计算机专业的朋友都…

OSI网络通信模型详解

OSI 模型就是把这整个过程拆解成了 7 个明确分工的步骤&#xff0c;每一层只负责自己那一摊事儿&#xff0c;这样整个系统才能顺畅运转&#xff0c;出了问题也容易找到“锅”在谁那。 核心比喻&#xff1a;寄快递 &#x1f4e6; 想象你要把一份重要的礼物&#xff08;你的数据…

C++ 检测文件大小和文件传输

检测文件的大小 你可以通过标准 C/C 的文件 API 很方便地获取文件的字节大小&#xff0c;以下是几种常用方法&#xff1a; ✅ 方法一&#xff1a;使用 stat() 函数&#xff08;推荐&#xff09; #include <sys/stat.h> #include <stdio.h>off_t get_file_size(co…

Ubuntu 中修改网卡 IP

在 Ubuntu 中修改网卡 IP 地址可以通过以下方法实现&#xff0c;具体取决于你使用的网络管理工具&#xff08;如 netplan、ifconfig/ip 命令或传统 interfaces 文件&#xff09;。以下是常见方法&#xff1a; 方法 1&#xff1a;使用 netplan&#xff08;Ubuntu 17.10 及更新版…

记录学习three.js 为什么 .glTF 是更适合 Web 的 3D 模型格式?——从 .OBJ 到 .glTF 的转变⑭

在上一篇中&#xff0c;我们介绍了如何在 Three.js 中加载 .OBJ 模型。如果你没看过&#xff0c;建议先阅读一下基础内容。然而你很快会发现&#xff0c;.OBJ 虽然入门简单&#xff0c;却并不是 Web3D 场景中的最佳格式。 .OBJ 是什么&#xff1f; .OBJ 是最早期的3D交换格式之…

H递归函数.go

前言&#xff1a;递归函数是一种强大而又充满魅力的编程技巧。它就像是一面神奇的镜子&#xff0c;函数在其中能够调用自身的倒影&#xff0c;从而以一种简洁而优雅的方式解决许多复杂的问题。 目录 一、递归函数是啥玩意儿 二、递归函数的优缺点 优点 缺点 三、递归函数…

软件功能测试的测试标准

一、软件功能测试行业标准概述 软件功能测试行业标准是规范软件测试流程、方法、工具及人员资质的准则&#xff0c;是确保软件产品的功能性、可靠性、易用性等质量特性符合用户需求。这些标准不仅为测试人员提供了明确的指导&#xff0c;也为软件产品的质量控制提供了有力保障。…