设计模式之原型模式详解

前言

在设计模式的系列文章中,我们前面已经写了工厂模式单列模式建造者模式,在针对创建型模式中,今天想跟大家分享的是原型模式,我觉的这种模式叫克隆模式会更佳恰当。原型模式的目的就是通过复制一个现有的对象来生成一个新的对象。

模式概述

原型模式

使用原型实例指定创建对象的种类,并且通过克隆这些原型创建新的对象,原型模式是一种对象的创建型模式

工作原理

将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象克隆自己来实现创建的过程,通过克隆方法所创建的对象是全新的对象,它们在内存中拥有新的地址;通常,对克隆产生的对象进行的修改不会对原型对象造成任何影响,每一个克隆的对象都是相互独立的。

原型模式的结构

原型模式模式分为两种,一种是不带管理类的原型模式,另一种是带管理类的原型模式。

下面这种是不带管理类的原型模式:

 

定义

原型模式包含了三个角色

  • Prototype(抽象原型类):它是声明克隆方法的接口,是所有具体原型类的公共父类,可以是抽象类也可以是接口,甚至可以是具体的实现类
  • ConcretePrototype(具体原型类):它实现在抽象原型类中声明的克隆方法,在克隆方法中返回一个自己的克隆对象
  • Client(客户类):让一个原型对象克隆自己从而创建一个新的对象,在客户类中只需要直接实例化或通过工厂方法等方式创建一个原型对象,再通过该对象的克隆方法即可获得多个相同的对象

需要注意的点:

在 Java 中 能够克隆的 Java类 务必得 实现 Cloneable 接口,表示这个 类 能够被 “复制”,至于这个 复制的效果 则与我们的实现有关,通常 clone()方法满足以下的条件:

  • 对任何的对象x,都有:x.clone()!=x 。换言之,克隆对象与元对象不是一个对象
  • 对任何的对象x,都有:x.clone().getClass==x.getClass(),换言之,克隆对象与元对象的类型一样
  • 对任何的对象x,如果 equals() 方法编写得当的话, 那么x.clone().equals(x)应该是成立的

在正式开始原型模式之前,我们先了解两个概念浅克隆和深克隆浅克隆和深克隆的主要区别在于是否支持引用类型的成员变量的复制

浅拷贝与深拷贝

A、浅拷贝

    被拷贝对象的所有变量都含有与原对象相同的值,而且对其他对象的引用仍然是指向原来的对象。即浅拷贝只负责当前对象实例,对引用的对象不做拷贝。

B、深拷贝

    被拷贝对象的所有的变量都含有与原来对象相同的值,除了引用其他对象的变量。引用其他对象的变量将指向一个被拷贝的新对象,而不再是原有被引用对象。即深拷贝把要拷贝的对象所引用的对象也都拷贝了一次。

    深拷贝要深入到多少层,是一个不确定的问题。在决定以深拷贝的方式拷贝一个对象的时候,必须决定对间接拷贝的对象是采取浅拷贝还是深拷贝还是继续采用深拷贝。因此,在采取深拷贝时,需要决定多深才算深。此外,在深拷贝的过程中,很可能会出现循环引用的问题。

我们通过一个实例来看一下具体的使用过程。

我们举一个大学里常见的例子,一个班里有一个学霸的话整个班级的作业就不用愁了,大家可以拿学霸的作业去复制嘛。

这个类是作业的抽象父类,定义了一些作业都要实现的方法,这里只实现了一个数学作业类,将来可以能有编程作业等。

package com.designpattern.prototype1;public abstract class Homework implements Cloneable {public abstract Object clone();public abstract void show();
}

数学作业的类要实现自己的复制逻辑,因为数学作业和编程作业的抄袭的方法肯定是不一样的。

package com.designpattern.prototype1;import java.util.Date;public class MathHomework extends Homework{/*** 这里只是用一个日期类来表示一下深度复制*/private Date A = new Date();private int a = 1;public void show() {System.out.println("Math clone");}/*** 实现自己的克隆方法*/public Object clone(){MathHomework m = null;/*** 深度复制*/m = (MathHomework) this.clone();m.A = (Date)this.getA().clone();return m;}public Date getA(){return A;}}

客户端就可以使用学霸的作业抄袭了

package com.designpattern.prototype1;public class Main {public static void main(String[] args){/*** 建立一个学霸,全班同学的作业就靠他了*/MathHomework xueba = new MathHomework();/*** 学渣都是从学霸那复制来的*/MathHomework xuezha = (MathHomework)xueba.clone();xuezha.show();}
}

那如果一个班里有两个学霸呢,那肯定班里的同学有的会超A同学的,有的会抄B同学的,这样的话系统里就必须要保留两个原型类,这时候使用我们的带有管理类的原型模式就比较方便了。

此时的结构图是这样的:

新增加的管理类:

package com.designpattern.prototype1;import java.util.Map;public class Manager {private static Manager manager;private Map prototypes = null;private Manager() {manager = new Manager();}//使用了简单工厂模式public static Manager getManager() {if (manager == null)manager = new Manager();return manager;}public void put(String name,Homework prototype){manager.put(name, prototype);}public Homework getPrototype(String name){if(prototypes.containsKey(name)){return (Homework) ((Homework)prototypes.get(name)).clone();}else{Homework homework = null;try{homework = (Homework)Class.forName(name).newInstance();put(name, homework);}catch(Exception e){e.printStackTrace();}return homework;}}}
package com.designpattern.prototype1;public class MainManager {public static void main(String[] args){/*** 建立一个学霸,全班同学的作业就靠他了*/MathHomework xueba = new MathHomework();Manager.getManager().put("com.designpattern.prototype1.MathHomework", xueba);/*** 学渣都是从学霸那复制来的*/MathHomework xuezha = (MathHomework) Manager.getManager().getPrototype("com.designpattern.prototype1.MathHomework");xuezha.show();}
}

简单形式和登记形式的原型模式各有其长处和短处,如果需要创建的原型对象数目较少 而且比较固定的话可以采取简单形式,如果创建的原型对象数目不固定的话建议采取第二种形式。

原型模式优缺点

优点:

  1. 原型模式对客户隐藏了具体的产品类
  2. 运行时刻增加和删除产品: 原型模式允许只通过客户注册原型实例就可以将一个新的具体产品类并入系统。
  3. 改变值以指定新对象: 高度动态的系统允许通过对象复合定义新的行为。如通过为一个对象变量指定值并且不定义新的类。通过实例化已有类并且将实例注册为客户对象的原型,就可以有效定义新类别的对象。客户可以将职责代理给原型,从而表现出新的行为。
  4. 改变结构以指定新对象:许多应用由部件和子部件来创建对象。
  5. 减少子类的构造,Prototype模式克隆一个原型而不是请求一个工厂方法去产生一个新的对象。
  6. 用类动态配置应用 一些运行时刻环境允许动态将类装载到应用中。
  7. 使用原型模式创建对象比直接new一个对象在性能上要好的多,因为Object类的clone方法是一个本地方法,直接操作内存中的二进制流,特别是复制大对象时,性能的差别非常明显。
  8. 使用原型模式的另一个好处是简化对象的创建,使得创建对象很简单。

缺点:

    原型模式的主要缺陷是每一个抽象原型Prototype的子类都必须实现clone操作,实现clone函数可能会很困难。当所考虑的类已经存在时就难以新增clone操作,当内部包括一些不支持拷贝或有循环引用的对象时,实现克隆可能也会很困难的。

适用场景

  1. 资源优化场景。
  2. 类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。
  3. 性能和安全要求的场景。
  4. 通过 new 产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。
  5. 一个对象多个修改者的场景。
  6. 一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。
  7. 在实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过 clone 的方法创建一个对象,然后由工厂方法提供给调用者。原型模式已经与 Java 融为浑然一体,大家可以随手拿来使用。

注意事项:与通过对一个类进行实例化来构造新对象不同的是,原型模式是通过拷贝一个现有对象生成新对象的。浅拷贝实现 Cloneable,重写,深拷贝是通过实现 Serializable 读取二进制流。 

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

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

相关文章

【Linux】生产者消费者模型

目录 什么是生产消费者模型 为什么要使用生产消费者模型 基于阻塞队列的生产消费者模型 什么是生产消费者模型 生产者消费者模型是一种常见的并发编程模型,用于解决生产者和消费者之间数据交换和同步的问题。在这个模型中,生产者会生成数据并将其放入…

Spring之AOP的特性

一. AOP简介 AOP是Aspect-Oriented Programming的缩写,即面向切面编程。利用oop思想,可以很好的处理业务流程,但是不能把系统中某些特定的重复性行为封装到模块中。例如,在很多业务中都需要记录操作日志,结果我们不得…

HTML5 游戏开发实战 | 五子棋

01、五子棋游戏设计的思路 在下棋过程中,为了保存下过的棋子的信息,使用数组 chessData。chessData[x][y]存储棋盘(x,y)处棋子信息,1 代表黑子,2 代表白子,0…

【100天精通python】Day38:GUI界面编程_PyQT从入门到实战(中)

目录 专栏导读 4 数据库操作 4.1 连接数据库 4.2 执行 SQL 查询和更新: 4.3 使用模型和视图显示数据 5 多线程编程 5.1 多线程编程的概念和优势 5.2 在 PyQt 中使用多线程 5.3 处理多线程间的同步和通信问题 5.3.1 信号槽机制 5.3.2 线程安全的数据访问 Q…

高效数据传输:轻松上手将Kafka实时数据接入CnosDB

本篇我们将主要介绍如何在 Ubuntu 22.04.2 LTS 环境下,实现一个KafkaTelegrafCnosDB 同步实时获取流数据并存储的方案。在本次操作中,CnosDB 版本是2.3.0,Kafka 版本是2.5.1,Telegraf 版本是1.27.1 随着越来越多的应用程序架构转…

Linux驱动开发之点亮三盏小灯

头文件 #ifndef __HEAD_H__ #define __HEAD_H__//LED1和LED3的硬件地址 #define PHY_LED1_MODER 0x50006000 #define PHY_LED1_ODR 0x50006014 #define PHY_LED1_RCC 0x50000A28 //LED2的硬件地址 #define PHY_LED2_MODER 0x50007000 #define PHY_LED2_ODR 0x50007014 #define…

TiDB基础介绍、应用场景及架构

1. 什么是newsql NewSQL 是对各种新的可扩展/高性能数据库的简称,这类数据库不仅具有NoSQL对海量数据的存储管理能力,还保持了传统数据库支持ACID和SQL等特性。 NewSQL是指这样一类新式的关系型数据库管理系统,针对OLTP(读-写&…

移动通信系统的LMS自适应波束成形技术matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab2022a 3.部分核心程序 ..................................................................... idxx0; while idxx&…

docker 基础知识

目录 1. 加载docker镜像 2. 显示所有的镜像 3. 执行镜像,生成容器, 每执行一次,便生成一个容器 4. 显示出container名称 5. 进入容器 6. 如何将文件传入容器内 首先要确保已经安装了docker。注意:服务器上若没有管理员权限&am…

(贪心) 剑指 Offer 14- II. 剪绳子 II ——【Leetcode每日一题】

❓剑指 Offer 14- II. 剪绳子 II 难度:中等 给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n 都是整数,n > 1 并且 m>1 ),每段绳子的长度记为 k[0],k[1]...k[m - 1] 。请问 k[0]*k[1]*.…

数据结构的图存储结构

目录 数据结构的图存储结构 图存储结构基本常识 弧头和弧尾 入度和出度 (V1,V2) 和 的区别,v2> 集合 VR 的含义 路径和回路 权和网的含义 图存储结构的分类 什么是连通图,(强)连通图详解 强连通图 什么是生成树,生…

小程序-基于vant的Picker组件实现省市区选择

一、原因 因vant/area-data部分的市/区数据跟后台使用的高德/腾讯省市区有所出入,故须保持跟后台用同一份数据,所以考虑以下几个组件 1、Area 2、Cascader 3、Picker 因为使用的是高德地图的省市区json文件,用area的话修改结构代价太大&…

解锁园区交通新模式:园区低速自动驾驶

在当今科技飞速发展的时代,自动驾驶技术成为了备受关注的领域之一。尤其是在园区内部交通管理方面,自动驾驶技术的应用正在日益受到重视。 园区低速自动驾驶的实现需要多个技术领域的协同合作,包括自动驾驶技术、计算机视觉技术、通信技术、物…

KVM虚拟机管理

1、创建、删除快照 关机 init0 列出快照 删除快照 2、虚拟机迁移 报错 解决:关闭防火墙,关闭selinux 其他解决办法:kvm热迁移使用nfs共享存储报错_莉法的博客-CSDN博客

神经网络基础-神经网络补充概念-14-逻辑回归中损失函数的解释

概念 逻辑回归损失函数是用来衡量逻辑回归模型预测与实际观测之间差异的函数。它的目标是找到一组模型参数,使得预测结果尽可能接近实际观测。 理解 在逻辑回归中,常用的损失函数是对数似然损失(Log-Likelihood Loss)&#xff…

网络安全 Day30-运维安全项目-容器架构上

容器架构上 1. 什么是容器2. 容器 vs 虚拟机(化) :star::star:3. Docker极速上手指南1)使用rpm包安装docker2) docker下载镜像加速的配置3) 载入镜像大礼包(老师资料包中有) 4. Docker使用案例1) 案例01::star::star::…

Redis-分布式锁!

分布式锁,顾名思义,分布式锁就是分布式场景下的锁,比如多台不同机器上的进程,去竞争同一项资源,就是分布式锁。 分布式锁特性 互斥性:锁的目的是获取资源的使用权,所以只让一个竞争者持有锁,这…

三分之一的英国大学生被欺诈

根据NatWest的一项新研究,去年英国大学三分之一的学生在网上遭遇欺诈。 今年5月,这家高街银行委托咨询公司RedBrick对来自63个城镇的3000多名英国大学生进行了调查。 尽管三分之一的受访者表示他们在过去的12个月里遇到过诈骗,但没有统计数…

【Unity每日一记】资源加载相关你掌握多少?

👨‍💻个人主页:元宇宙-秩沅 👨‍💻 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 👨‍💻 本文由 秩沅 原创 👨‍💻 收录于专栏:uni…

opencv进阶08-K 均值聚类cv2.kmeans()介绍及示例

K均值聚类是一种常用的无监督学习算法,用于将一组数据点分成不同的簇(clusters),以便数据点在同一簇内更相似,而不同簇之间差异较大。K均值聚类的目标是通过最小化数据点与所属簇中心之间的距离来形成簇。 当我们要预测…