设计模式 - 原型模式,就该这样学!

 

目录

开始

为什么要引入原型模式

原型模式概述

原型模式代码实现(浅拷贝)

浅拷贝和深拷贝的区别

原型模式代码实现(深拷贝)

方式一:直接 copy

方式二:序列化和反序列化(推荐)


开始


为什么要引入原型模式

a)问题:现在有一个小猪猪,名字为 lyj,年龄为:3,颜色为:粉色,请编写程序创建和 lyj 猪猪 属性完全相同的 10 头猪猪.

b)传统的处理方式如下:

基于 lyj 这个猪猪,直接 new 出 10 只一样的猪猪.

data class Pig(val name: String,val age: Int,val color: String,
)fun main() {val pig = Pig("lyj", 3, "粉色")val p1 = Pig(pig.name, pig.age, pig.color)val p2 = Pig(pig.name, pig.age, pig.color)val p3 = Pig(pig.name, pig.age, pig.color)val p4 = Pig(pig.name, pig.age, pig.color)//...println(p1)println(p2)println(p3)println(p4)//...}

c)传统方式创建的优缺点:

优点:

  • 好理解,操作简单.

缺点:

  • 创建新的对象时,总是需要重新获取原始对象的属性,如果创建的对象比较复杂时,效率比较低(可能含有大量的计算操作).
  • 总是需要重新初始化对象,而不是动态的获取对象运行时的状态,不够灵活.

原型模式概述

原型模式(Prototype Pattern)是一种创建型的设计模式,运行一个对象通过复制自身的实例来创建新的对象.   简而言之,提供了一个快速创建新对象的方法,这个新对象是已有对象的精确复制品(根据具体的业务场景,决定是深拷贝 还是 浅拷贝).

优点:

  • 简化对象创建:复制复杂对象时,可以通过原型模式简化对象的复制过程,并且可以动态的配置对象状态.
  • 性能提升:同时提升了对象创建的速度(某些情况下,复制现有对象比创建新对象更节省资源,例如大型对象创建可能涉及到大量的计算 或者 I/O 操作,原型模式可以避免这些重复初始化的资源开销,直接复制即可).
  • 解耦合,高扩展:如果原始对象发生变化(增加或者减少属性),其他克隆对象也会发生响应的变化,无需修改代码.

缺点:

  • 由于要对被复制的类配备一个克隆方法,因此如果是对老代码的改造,就需要修改代码,违背了开闭原则.

角色如下:

  • 原型类(Prototype):原型类,声明一个克隆自己的接口.
  • 具体的原型类(ConcretePrototype):实现一个克隆自己的操作.
  • 客户端(Cline):让一个原型对象克隆自己,从而创建一个新的对象(属性一样).

原型模式代码实现(浅拷贝)

a)猪猪类先实现克隆接口

data class Pig(val name: String,val age: Int,val color: String,
) {/*** Kotlin 的数据类中默认提供了 copy 方法用于浅拷贝*/fun clone(): Any {return this.copy()}}

b)客户端

fun main() {val pig = Pig("lyj", 3, "pink")val p1 = pig.clone()val p2 = pig.clone()val p3 = pig.clone()//...println("$p1, ${p1.hashCode()}")println("$p2, ${p2.hashCode()}")println("$p3, ${p3.hashCode()}")//...
}

c)运行结果如下:

 

d)注意:

kotlin 的数据类默认提供了 copy() 方法,用于实现对这个对象的 浅拷贝,但如果在 copy 时传入其他参数,就会为当前对象创建新对象,但是对内的引用变量还是传入地址(Pig 的 hashCode 不一致,单 child 的hashCode 一致)

例如,复制当前对象并修改 name

data class Pig(var name: String,val age: Int,val color: String,val child: PigChild
) {/*** kotlin 的数据类默认提供了 copy() 方法,用于实现对这个对象的 浅拷贝* 但如果在 copy 时传入其他参数,就会为当前对象创建新对象,但是对内的引用变量还是传入地址(Pig 的 hashCode 不一致,单 child 的hashCode 一致)* 例如,复制当前对象并修改 name*/fun copyWithId(name: String): Pig {return this.copy(name = name)}}data class PigChild (var name: String,
)fun main() {val p = Pig("lyj", 1, "pink", PigChild("aaa"))val p2 = p.copy(name = "ccc")println("${p.hashCode()}, ${p.child.hashCode()}")println("${p2.hashCode()}, ${p2.child.hashCode()}")
}

执行如下: 

 

 

浅拷贝和深拷贝的区别

成员变量中有 基本数据类型 和 引用数据类型.

a)浅拷贝

基本数据类型:复制 值 ,给新开辟空间的成员变量.

引用数据类型:复制 地址,给新开辟空间的成员变量.

b)深拷贝

基本数据类型:复制 值 ,给新开辟空间的成员变量.

引用数据类型:复制 该引用对象中所有的成员变量所引用的对象,直到所有可达的对象.  也就是对整个对象进行拷贝.

c)可以这样理解,浅拷贝只是对最外层的对象进行了 1 层拷贝,而深拷贝则是对象中无论有多少嵌套对象,都会进行拷贝(进行了 n 层拷贝).

原型模式代码实现(深拷贝)

方式一:直接 copy

实际上就是对成员变量中的所有 引用类型 都使用 copy 进行一遍处理.

data class Child(val name: String)data class Parent(val name: String, val child: Child) {fun deepCopy(): Parent {return this.copy(child = this.child.copy())}
}fun main() {val p = Parent("John", Child("New York"))val p1 = p.deepCopy()println(p == p1) //trueprintln(p === p1) //falseprintln(p.child === p1.child) //false
}

 

但是这种方式的缺点就是,将来如果 Parent 中有很多个成员变量都是引用对象 ,将来必须要一个个都进行 copy.

Ps:kotlin 中 == 表示比较两个对象的内容是否相等.  === 表示两个引用是否指向同一个对象.

方式二:序列化和反序列化(推荐)

推荐使用这种方式.

如果类中有多个引用对象,就不需要一个个处理了.

data class Child(val name: String
): Serializabledata class Parent(val name: String,val child: Child
): Serializable {fun deepCopy(): Parent {//序列化val bo = ByteArrayOutputStream()ObjectOutputStream(bo).use { it.writeObject(this) }//反序列化val bi = ByteArrayInputStream(bo.toByteArray())return ObjectInputStream(bi).use { it.readObject() as Parent }}}fun main() {val p = Parent("John", Child("New York"))val p1 = p.deepCopy()println(p == p1) //trueprintln(p === p1) //falseprintln(p.child === p1.child) //false
}

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

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

相关文章

LeetCode 1527, 54,114

目录 1527. 患某种疾病的患者题目链接表要求知识点思路代码 54. 螺旋矩阵题目链接标签思路代码 114. 二叉树展开为链表题目链接标签前序遍历思路代码 前驱思路代码 1527. 患某种疾病的患者 题目链接 1527. 患某种疾病的患者 表 表Patients的字段为patient_id、patient_name…

C++ | Leetcode C++题解之第205题同构字符串

题目&#xff1a; 题解&#xff1a; class Solution { public:bool isIsomorphic(string s, string t) {unordered_map<char, char> s2t;unordered_map<char, char> t2s;int len s.length();for (int i 0; i < len; i) {char x s[i], y t[i];if ((s2t.coun…

Webpack: 7 款常用的性能分析工具

概述 Webpack 最大的优势在于它的功能非常强大、全面&#xff0c;加之繁荣活跃的组件生态&#xff0c;已经足够应对几乎所有 Web 构建需求&#xff0c;包括&#xff1a;SPA、MPA、SSR、桌面应用、Node 程序、WebAssemsbly、PWA、微前端等等&#xff0c;所以即使在近几年工程化…

Python | Leetcode Python题解之第205题同构字符串

题目&#xff1a; 题解&#xff1a; class Solution:def isIsomorphic(self, s: str, t: str) -> bool:dicts Counter(s)dictt Counter(t) if list(dicts.values()) ! list(dictt.values()):return Falsefor i in range(len(s)):inds list(dicts.keys()).index(s…

C#语言编写的仅有8KB大小的简易贪吃蛇开源游戏

前言 今天大姚给大家分享一款由C#语言编写的仅有8KB大小的简易贪吃蛇开源游戏&#xff1a;SeeSharpSnake。 项目特点 该仓库中的项目文件和脚本可以用多种不同的配置构建相同的游戏&#xff0c;每个配置生成的输出大小也不同。 项目源码运行 F5 运行 SeeSharpSnake项目&…

E4A视频APP源码电视盒子Coolpoint酷点电视4.5版后端苹果cms

电视盒子酷点TV版4.5影视APP源码后端对接苹果CMS&#xff0c;此版带会员功能,对接的是 如意验证1.71版苹果cms后端APP 电视TV4.5版,会员功能带注册邀请,绑定邮箱,邮箱找回密码,修改新密码,卡密充值,签到,积分兑换会员&#xff1b; 自带10个解析线路,有电视直播功能,首页滚动公…

Java教程之IO模式精讲,NIO+BIO

第一章 BIO、NIO、AIO介绍 背景 在java的软件设计开发中&#xff0c;通信架构是不可避免的&#xff0c;我们在进行不同系统或者不同进程之间的数据交互&#xff0c;或 者在高并发下的通信场景下都需要用到网络通信相关的技术&#xff0c;对于一些经验丰富的程序员来说&#x…

第11章 规划过程组(11.5创建WBS)

第11章 规划过程组&#xff08;一&#xff09;11.5创建WBS&#xff0c;在第三版教材第380~383页&#xff1b; 文字图片音频方式 视频22 第一个知识点&#xff1a;主要输入 1、项目管理计划 范围管理计划 定义了如何根据项目范围说明书创建WBS2、项目文件 项目范围说明…

十六进制计数器

目录 描述 输入描述&#xff1a; 输出描述&#xff1a; 参考代码 描述 请用Verilog设计十六进制递增计数器电路&#xff0c;每个时钟周期递增1。 电路的接口如下图所示。Q[3:0]中&#xff0c;Q[3]是高位。 接口电路图如下&#xff1a; 输入描述&#xff1a; input …

操作系统期末复习考题三(不挂科系列)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、前言&#x1f680;&#x1f680;&#x1f680;二、正文☀️☀️☀️三、总结&#x1f353;&#x1f353;&#x1f353; 一、前言&#x1f680;&#x1f680;&am…

java基于ssm+jsp 房屋租赁系统

1 管理员登录 管理员输入个人的用户名、密码登录系统&#xff0c;这时候系统的数据库就会在进行查找相关的信息&#xff0c;如果我们输入的用户名、密码不正确&#xff0c;数据库就会提示出错误的信息提示&#xff0c;同时会提示管理员重新输入自己的用户名、密码&#xff0c;…

了解WPF控件:OpenFileDialog常用属性与用法(十六)

掌握WPF控件&#xff1a;熟练OpenFileDialog常用属性&#xff08;十六&#xff09; OpenFileDialog控件在WPF中用于需要用户指定文件路径&#xff0c;为用户提供了一个直观且易用的界面来浏览和选择本地文件系统中的文件。例如&#xff0c;当用户需要打开一个已存在的文本文件…

C++自定义智能指针

template <class T> class counted_ptr;// 智能指针引用计数类 template <class T> class Ref_Ptr {friend class counted_ptr<T>; private:T* m_pTtr; // 实际的指针size_t counted_ptr; // 引用计数Ref_Ptr(T* p);virtual ~Ref_Ptr(); };template <clas…

【Arduino】实验使用ESP32控制可编程继电器制作跑马灯(图文)

今天小飞鱼实验使用ESP控制继电器&#xff0c;为了更好的掌握继电器的使用方法这里实验做了一个跑马灯的效果。 这里用到的可编程继电器&#xff0c;起始原理并不复杂&#xff0c;同样需要ESP32控制针脚输出高电平或低电平给到继电器&#xff0c;继电器使用这个信号控制一个电…

如何焊铜管 量测射频前端模块

先说结论 要做Port Extension待测物要上电 且根据逻辑表给Enable pin上电网分输入功率 不要太大 -20dBm即可铜管的接地 要足够 以及足够近铜管与待测物之间 必要时 隔一颗电容不要将匹配元件 也包含在量测范围讯号针不要直接焊在焊盘上 首先 铜管要做Port…

50-3 内网信息收集 - 域环境搭建

搭建准备: 在搭建准备阶段,我们需要准备三台 Windows 虚拟机:Windows Server 2012、Windows 7 和 Windows Server 2008。接下来,我们将配置 Windows Server 2012 作为域控制器,而 Windows 7 和 Windows Server 2008 将作为成员机加入域。建议保持这三台虚拟机的内存不超过…

(漏洞检查项) | 任意文件包含漏洞 file-include

(漏洞检查项)|任意文件包含漏洞 file-include 漏洞场景 1.含有动态包含语句 2.有类似于文件读取的url 漏洞描述 攻击者可以利用任意文件包含漏洞&#xff0c;读取任意文件&#xff0c;对服务器造成危害。 程序开发人员为了代码的灵活性&#xff0c;常常会将包含文件的路径…

influxdb时序数据库使用

influxdb时序数据库使用 1.1.免费无云influx申请1.2.Telegraf安装1.3.influxdb安装mac安装Redhat && Centos安装docker安装Kubernetes安装windows安装 1.4.influx CLI 安装1.5.influx命令行界面1.5.influx配置项权限认证配置管理 API 令牌 InfluxDB 是一个开源分布式时…

用通俗易懂方式讲解:快速部署大模型 ChatGLM3 并进行推理

在深入了解了一些大模型的知识之后&#xff0c;最好的方法是亲自动手搭建一个开源的大模型&#xff0c;以更深入地理解其工作原理。 在此基础上&#xff0c;我们将以 ChatGLM3 为例进行部署及推理&#xff0c;从而进一步探索大模型的应用和实践。 ChatGLM3简介&#xff1a; …

Alibaba Cloud Linux详解_操作系统兼容性_alinux稳定性全解析

Alibaba Cloud Linux是阿里云自研的稳定、安全、高性能的服务器Linux操作系统&#xff0c;完全兼容CentOS/RHEL生态和操作方式&#xff0c;又阿里云提供免费提供长期支持和维护LTS。Alibaba Cloud Linux是目前阿里云服务器最大规模使用的操作系统之一&#xff0c;可部署在Web网…