程序里对象很深很大,可以用这个设计模式缓解一下

news/2024/5/14 17:49:22/文章来源:https://blog.csdn.net/m0_73257876/article/details/126927242

如果一个类的有非常多的属性,层级还很深。这个妥妥的是我的对象很大,你创建的时候忍一下......那你每次要创建的时候都忍一下?有没有一种好的方式让我们创建太的时候使用体验更好一点呢? 今天的文章里就给大家介绍一种设计模式,来解决这个问题。

这篇内容要说的是创造型设计模式里的原型模式,其实在 Java、Go之类的编程语言里其实原型模式用的并不频繁并不太常用,如果写过点 JS 代码的话,大家可能听说过原型链这么个东西,原型模式在 JavaScript 实现里确实广泛应用,它那个面向对象跟 Java、C++ 这些语言的面向对象的实现方式还不太一样,继承其实是通过原型克隆出来,在拷贝出来的原型的基础上再继续添加或者修改来实现的。

什么是原型模式

通过复制、拷贝或者叫克隆已有对象的方式来创建新对象的设计模式叫做原型模式,被拷贝的对象也被称作原型对象。

原型对象按照惯例,会暴露出一个 Clone 方法,给外部调用者一个机会来从自己这里“零成本”的克隆出一个新对象。

这里的“零成本”说的是,调用者啥都不用干,干等着,原型对象在 Clone 方法里自己克隆出自己,给到调用者,所以按照这个约定所有原型对象都要实现一个 Clone 方法。

type Prototype interface {Clone() SpecificType
}

这里我们用UML类图描述一下原型模式中各角色拥有的行为以及它们之间的关系

原型模式--UML类图

至于原型对象克隆自己的时候用的是深拷贝还是浅拷贝?可以先理解成是都用深拷贝,等完全掌握这种思想后,可以再根据实际情况,比如为了节省空间、以及减少编写克隆方法的复杂度时可以两者综合使用。

原型模式更多的是阐述一种编程模式,并没有限制我们用什么方式实现。比如下面这个深拷贝和浅拷贝结合使用的例子。

// 示例代码来自:https://lailin.xyz/post/prototype.html
package prototypeimport ("encoding/json""time"
)// Keyword 搜索关键字
type Keyword struct {word      stringvisit     intUpdatedAt *time.Time
}// Clone 这里使用序列化与反序列化的方式深拷贝
func (k *Keyword) Clone() *Keyword {var newKeyword Keywordb, _ := json.Marshal(k)json.Unmarshal(b, &newKeyword)return &newKeyword
}// Keywords 关键字 map
type Keywords map[string]*Keyword// Clone 复制一个新的 keywords
// updatedWords: 需要更新的关键词列表,由于从数据库中获取数据常常是数组的方式
func (words Keywords) Clone(updatedWords []*Keyword) Keywords {newKeywords := Keywords{}for k, v := range words {// 这里是浅拷贝,直接拷贝了地址newKeywords[k] = v}// 替换掉需要更新的字段,这里用的是深拷贝for _, word := range updatedWords {newKeywords[word.word] = word.Clone()}return newKeywords
}

使用原型模式的目的

使用原型模式的目的主要是为了节省创建对象所花费的时间和资源消耗,提升性能。

还有一点就是,比如全局配置对象这种也可以当成原型对象,如果不想让程序在运行时修改初始化好的原型对象,导致影响其他线程的程序执行的时候,也可以用原型模式快速拷贝出一份原型对象,再做运行时自定义修改。

使用场景

当对象的创建成本比较大,并且同一个类的不同对象间差别不大时(大部分属性值相同),如果对象的属性值需要经过复杂的计算、排序,或者需要从网络、DB等这些慢IO中获取、亦或者或者属性值拥有很深的层级,这时就是原型模式发挥作用的地方了。

因为对象在内存中复制自己远比每次创建对象时重走一遍上面说的操作要来高效的多。

下面再来一个例子,让我们更好的理解原型模式的优点。

利用原型模式实现文档树

下面是一个类似 DOM 树对象的例子,因为 DOM 对象往往层级会很深,那么要创建类似的DOM树的时候能让我们更好的理解原型模式的优势。

这个示例代码来自:https://blog.ralch.com/articles/design-patterns/golang-prototype/

package domimport ("bytes""fmt"
)// Node a document object model node
type Node interface {// Strings returns nodes text representationString() string// Parent returns the node parentParent() Node// SetParent sets the node parentSetParent(node Node)// Children returns the node children nodesChildren() []Node// AddChild adds a child nodeAddChild(child Node)// Clone clones a nodeClone() Node
}// Element represents an element in document object model
type Element struct {text     stringparent   Nodechildren []Node
}// NewElement makes a new element
func NewElement(text string) *Element {return ∈{text:     text,parent:   nil,children: make([]Node, 0),}
}// Parent returns the element parent
func (e *Element) Parent() Node {return e.parent
}// SetParent sets the element parent
func (e *Element) SetParent(node Node) {e.parent = node
}// Children returns the element children elements
func (e *Element) Children() []Node {return e.children
}// AddChild adds a child element
func (e *Element) AddChild(child Node) {copy := child.Clone()copy.SetParent(e)e.children = append(e.children, copy)
}// Clone makes a copy of particular element. Note that the element becomes a
// root of new orphan tree
func (e *Element) Clone() Node {copy := ∈{text:     e.text,parent:   nil,children: make([]Node, 0),}for _, child := range e.children {copy.AddChild(child)}return copy
}// String returns string representation of element
func (e *Element) String() string {buffer := bytes.NewBufferString(e.text)for _, c := range e.Children() {text := c.String()fmt.Fprintf(buffer, "\n %s", text)}return buffer.String()
}

上面的DOM对象-- Node、Element 这些都支持原型模式要求的 Clone 方法,那么有了这个原型克隆的能力后,假如我们想根据创建好的 DOM 树上克隆出一个子分支作为一颗独立的 DOM 树对象的时候,就可以像下面这样简单地执行 Node.Clone() 把节点和其下面的子节点全部拷贝出去。比我们使用构造方法再重新构造树形结构要方便许多。

下面的例子是用DOM树结构创建一下公司里的职级关系,然后还可以从任意层级克隆出一颗新的树。

func main() {// 职级节点--总监directorNode := dom.NewElement("Director of Engineering")// 职级节点--研发经理engManagerNode := dom.NewElement("Engineering Manager")engManagerNode.AddChild(dom.NewElement("Lead Software Engineer"))// 研发经理是总监的下级directorNode.AddChild(engManagerNode)directorNode.AddChild(engManagerNode)// 办公室经理也是总监的下级officeManagerNode := dom.NewElement("Office Manager")directorNode.AddChild(officeManagerNode)fmt.Println("")fmt.Println("# Company Hierarchy")fmt.Print(directorNode)fmt.Println("")// 从研发经理节点克隆出一颗新的树fmt.Println("# Team Hiearachy")fmt.Print(engManagerNode.Clone())
}

原型模式总结

关于原型模式的总结,我们先来说一下原型模式的优缺点。

原型模式的优点

  • 某些时候克隆比直接new一个对象再逐属性赋值的过程更简洁高效,比如创建层级很深的对象的时候,克隆比直接用构造会方便很多。
  • 可以使用深克隆方式保存对象的状态,可辅助实现撤销操作。

原型模式的缺点

  • clone方法位于类的内部,当对已有类进行改造的时候,需要修改代码,违背了开闭原则。
  • 当实现深克隆时,需要编写较为复杂的代码,尤其当对象之间存在多重嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆。因此,深克隆、浅克隆需要运用得当。

在项目中使用原型模式时,可能需要在项目初始化时就把提供克隆能力的原型对象创建好,在多线程环境下,每个线程处理任务的时候,用到了相关对象,可以去原型对象那里拷贝。不过适合当作原型对象的数据并不多,所以原型模式在开发中的使用频率并不高,如果有机会做项目架构,可以适当考虑,确实需要再在项目中引入这种设计模式。

 

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

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

相关文章

C++多线程的线程返回值问题

对于多线程可执行对象的返回值是何时返回,以及得到的呢? 对于需要用到线程返回值的线程要使用future类对象来实现 文章目录future对象async()launch::deferred参数launch::async参数packaged_taskpromisefuture对象 是一个类模板 提供访问异步对象的操作…

优化 | Management Science 7-8月文章精选: 信息系统中的运筹学

作者:Evelyn Yao 清华大学本科在读 在“Management Science近期论文精选”中,我们有主题、有针对性地选择了Management Science中一些有趣的文章,不仅对文章的内容进行了概括与点评,而且也对文章的结构进行了梳理,旨在…

非零基础自学Java (老师:韩顺平) 第13章 常用类 13.5 StringBuffer类

非零基础自学Java (老师:韩顺平) ✈【【零基础 快速学Java】韩顺平 零基础30天学会Java】 第13章 常用类 文章目录非零基础自学Java (老师:韩顺平)第13章 常用类13.5 StringBuffer类13.5.1 基本介绍13.5.2 String VS StringBuffer13.5.3 String 和 Str…

HashMap

1.HashMap集合 1.1HashMap集合概述和特点【理解】 HashMap底层是哈希表结构的依赖hashCode方法和equals方法保证键的唯一如果键要存储的是自定义对象,需要重写hashCode和equals方法 1.2 特点 HashMap是线程不安全的实现; HashMap可以使用null作为key…

【Pytorch深度学习实战】(9)神经语言模型(RNN-LM)

🔎大家好,我是Sonhhxg_柒,希望你看完之后,能对你有所帮助,不足请指正!共同学习交流🔎 📝个人主页-Sonhhxg_柒的博客_CSDN博客 📃 🎁欢迎各位→点赞…

第一视角体验搭载全志T507-H的开发板MYD-YT507H开发板

如今车规级芯片市场潜力巨大,需求旺盛,芯片都在逐渐走向国产化。本文要介绍的主角是MYD-YT507H开发板,该开发板是米尔科技结合全志国产工业级平台CPU——全志T507-H芯片研制的CPU模组,全志T507-H可广泛用于电力物联网、汽车电子、…

目标检测开源框架YOLOv6全面升级,更快更准的2.0版本来啦

9月5日,美团视觉智能部发布了YOLOv6 2.0版本,本次更新对轻量级网络进行了全面升级,量化版模型 YOLOv6-S 达到了 869 FPS,同时,还推出了综合性能优异的中大型网络(YOLOv6-M/L),丰富了…

一个div靠左另一个靠右

1.使用flex布局<style>#back{border: red solid 1px;width: 800px;height: 500px;display: flex;align-items: center;}#left{border: blue 1px solid;width: 100px;height: 100px;justify-content: flex-start;}#right{border: blue 1px solid;width: 100px;height: 100…

【前端进阶】-TypeScript类型声明文件详解及使用说明

前言 博主主页&#x1f449;&#x1f3fb;蜡笔雏田学代码 专栏链接&#x1f449;&#x1f3fb;【TypeScript专栏】 前三篇文章讲解了TypeScript的一些高级类型 详细内容请阅读如下&#xff1a;&#x1f53d; 【前端进阶】-TypeScript高级类型 | 泛型约束、泛型接口、泛型工具类…

Google Pub/Sub入门

什么是Google Pub/Sub&#xff1f; 首先他是一个messaging buffer/coupler消息缓冲区/耦合器&#xff0c;Decouples senders and receivers解耦发送者和接收者。 一些特性&#xff1a; 使用 Dataflow 注入分析事件并将其流式插入到 BigQuery免运维、安全、可伸缩的消息传递系…

MySQL基础总结合集

MySQL是啥&#xff1f;数据库又是啥&#xff1f; MySQL&#xff1a; MySQL 是最流行的关系型数据库管理系统&#xff0c;在 WEB 应用方面 MySQL 是最好的 RDBMS(Relational Database Management System&#xff1a;关系数据库管理系统)应用软件之一。 数据库&#xff1a; 数…

基于nodejs+vue的读书会网站

实行网上读书会网站&#xff0c;对其改善目前人们读书现状提供一些帮助和优化措施&#xff0c;为人们在未来看书节约了很多时间&#xff0c;使得人们在未来利用自己有限的时间可以看到更多对自己有益的书籍。 基于Vue的读书会网站的实现&#xff0c;通过网上系统的研发构造&…

你是否想过,GitHub Pages也可以自动构建?|原创

本文讲述了如何利用 GitHub Actions 来自动构建 GitHub Pages 项目&#xff0c;免去繁琐的手动构建再提交过程&#xff0c;让你专注于写作。点击上方“后端开发技术”&#xff0c;选择“设为星标” &#xff0c;优质资源及时送达GitHub Actions 自动构建之前的文章我们已经讲过…

Tomcat 在IDEA中运行Tomcat,控制台乱码问题的解决方案

IDEA中运行Tomcat,控制台乱码问题的解决方案试了好多种网上的方案(只有这一种能解决)环境:jdk 11 idea 2022.2.4 tomcat 9.0.54解决方案: 1.打开tomcat的配置文件(apache-tomcat-9.0.54\conf\logging.properties)将文件中的java.util.logging.ConsoleHandler.encoding =…

el-tree增加提示语

element ui tree树形控件加提示信息<el-tree :data="tieLinedata" :props="defaultProps" @node-click="handleNodeClick"><span class="span-ellipsis" slot-scope="{ node, data }"><span :title="no…

【图像增强】基于DEHAZENET和HWD的水下去散射图像增强附matlab代码

1 内容介绍 去散射和边缘增强是解决水下图像的对比度严重衰减、颜色偏差和边缘模糊等问题的关键步骤。这篇论文提出了一种较好的水下图像增强的方法。首先使用经过端到端训练的卷积神经网络去测量输入图片&#xff0c;同时以自适应双边滤波器对传输图片进行处理。接着提出一种…

allure介绍——生成完美的测试报告

一、allure简介 Allure是输出网页测试报告的一种框架 1、该框架是基于Java写的,所以安装该框架需要先安装JDK; 2、下载allure命令行工具,路径:https://github.com/allure-framework/allure2/releases 注:①下载包放到pytest文件夹中,然后将allure/bin的路径放到环境变量的…

css font-size设置小于12px失效(转)

原文:https://blog.csdn.net/weixin_38629529/article/details/119866495 1、描述 不知道你有没有遇到这样的情况,设置了font-size为10px,打开控制台审查元素也显示的是10px,但浏览器渲染的字体大小还是没有发生改变。 这是因为浏览器(以Chrome为例,其他没测试过)在中文…

第五篇、Callable接口实现多线程

文章目录前言一、实现Callable接口二、代码示例1.Callable接口实现多线程总结前言 上一篇我们共同认识了并发问题&#xff0c;那么本篇我们将一起来学习Callable接口实现多线程。 一、实现Callable接口 上篇内容我们通过实现Runnable实现多线程&#xff0c;本篇我们将学习如何…

非零基础自学Java (老师:韩顺平) 第13章 常用类 13.11 日期类

非零基础自学Java (老师&#xff1a;韩顺平) ✈【【零基础 快速学Java】韩顺平 零基础30天学会Java】 第13章 常用类 文章目录非零基础自学Java (老师&#xff1a;韩顺平)第13章 常用类13.11 日期类13.11.1 第一代日期类13.11.2 第二代日期类13.11.3 第三代日期类13.11.4 Dat…