Android设计模式详解之原型模式

news2025/1/21 7:17:36

前言

原型模式是一个创建型设计模式

定义:用原型实例指定创建对象的种类,并通过复制这些原型创建新的对象。

使用场景:

  1. 类初始化需要消耗非常多的资源,这个资源包括数据、硬件资源,通过原型复制避免这些消耗;
  2. 通过new产生一个对象需要非常繁琐的数据准备或访问权限;
  3. 一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式复制多个对象供调用者使用,即保护性拷贝。

注意:通过实现Cloneable接口的原型模式在调用clone函数构造实例时并不一定比通过new操作速度快,只有当通过new构造对象较为耗时时或者说成本较高时,通过clone方法才能获取效率上的提升。
另外,实现原型模式也不一定非要实现cloneable接口,也有其他实现方式。

UML类图:
原型模式UML
- Client:客户端用户;
- Prototype:抽象类或者接口,声明具备clone能力;
- ConcretePrototype:具体的原型类;

实现示例

以文档拷贝为例,一个文档中包含文字和图片,编辑后的文档为了安全起见,需要进行文档拷贝,然后再在文档副本上进行修改,这里我们就可以使用原型模式来实现。

package com.crystal.essayjoker.clone

/**
 * Word文档类
 * on 2022/12/21
 */
class Word : Cloneable {

    /**
     * 文本
     */
    private var text: String? = null

    /**
     * 多张图片
     */
    private var images: ArrayList<String> = arrayListOf()

    constructor() {
        println("Word Class constructor execute!")
    }

    public override fun clone(): Word {
        val cloneWord = super.clone() as Word
        cloneWord.text = this.text
        cloneWord.images = this.images
        return cloneWord
    }

    fun setText(text: String) {
        this.text = text
    }

    fun getText(): String? {
        return text
    }

    fun addImage(image: String) {
        images.add(image)
    }

    fun getImages(): List<String> {
        return images
    }

    fun showWord() {
        println("-------showWord Start------")
        println("this word text is:$text")
        for (image in images) {
            println("this word image is:$image")
        }
        println("-------showWord End-------")
        println()
    }
}

编写测试类:

object Test {
    @JvmStatic
    fun main(args: Array<String>) {
        val originWord = Word()
        //构造数据
        originWord.setText("this is origin word")
        originWord.addImage("first image")
        originWord.addImage("second image")
        originWord.addImage("third image")
        //展示数据
        originWord.showWord()

        //拷贝文档副本
        val cloneWord = originWord.clone()
        cloneWord.showWord()
        //修改clone word text
        cloneWord.setText("this is clone word")
        cloneWord.showWord()
        //查看原来的word是否改变
        originWord.showWord()
    }
}

最终打印结果如下:
测试结果
从上面实例我们可以看出:

  1. 通过clone拷贝对象时并不会执行构造函数;
  2. 修改clone后的数据并不会影响原始数据?

对于第2条事实真的如此吗?我们尝试修改images对象中的数据:

 val originWord = Word()
        //构造数据
        originWord.setText("this is origin word")
        originWord.addImage("first image")
        originWord.addImage("second image")
        originWord.addImage("third image")
        //展示数据
        originWord.showWord()

        //拷贝文档副本
        val cloneWord = originWord.clone()
        cloneWord.showWord()
        //修改clone word text
        cloneWord.setText("this is clone word")
        cloneWord.addImage("fourth image")  //新增一条数据
        cloneWord.showWord()
        //查看原来的word是否改变
        originWord.showWord()

打印结果如下:
修改image数据
可以看到修改clone中的image数据,原始数据发生了改变!这就引出了两个概念:浅拷贝深拷贝

浅拷贝和深拷贝

浅拷贝:对基本数据类型进行值传递,对引用数据类型进行引用传递般的拷贝,此为浅拷贝。

深拷贝:对基本数据类型进行值传递,对引用数据类型,创建一个新的对象,并复制其内容,此为深拷贝。

对于上述原型模式的实现只是一个浅拷贝,也称为影子拷贝,实际上只是将副本数据字段引用指向了原始文档的字段,并没有重新构造一个image对象,只是将数据添加到了原来的image对象中

那想解决上述问题,我们就可以采用深拷贝的方式,修改Word类中clone方法如下:

    public override fun clone(): Word {
        val cloneWord = super.clone() as Word
        cloneWord.text = this.text
        cloneWord.images = this.images.clone() as ArrayList<String> //对image对象进行深拷贝
        return cloneWord
    }

Android源码中的原型模式

对应Intent我们在熟悉不过了,我们直接来看看它的clone方法实现:

	### Intent.clone()
   public Object clone() {
        return new Intent(this); //可以看到是直接new了个intent对象
    }

	### Intent(this)
	  public Intent(Intent o) {
        this(o, COPY_MODE_ALL);
    }
	
	### this(o, COPY_MODE_ALL)
    private Intent(Intent o, @CopyMode int copyMode) {
        this.mAction = o.mAction;
        this.mData = o.mData;
        this.mType = o.mType;
        this.mIdentifier = o.mIdentifier;
        this.mPackage = o.mPackage;
        this.mComponent = o.mComponent;
        if (o.mCategories != null) {
            this.mCategories = new ArraySet<>(o.mCategories);
        }
		...
		 if (o.mExtras != null) {
                    this.mExtras = new Bundle(o.mExtras);
                }
          ...
        }
    }

可以看出Intent.clone的实现是通过原型模式进行深拷贝,而且并不是通过cloneable 接口来实现的,是通过new出一个新的对象来实现的,具体采用哪种方式需要根据构造对象的成本来决定。

总结

原型模式本质上就是对象拷贝,需要注意的就是深浅拷贝的问题。可以解决复杂对象构建的资源消耗,提升创建对象的效率,另外一个重要的用途就是保护性拷贝,可以通过返回一个对象拷贝的形式实现只读的限制;

优点:
原型模式是对内存中二进制流的拷贝,比直接new性能好很多,特别是要在一个循环体内产生大量的对象时

缺点:
直接在内存中拷贝,构造函数是不会执行的,这点需要特别注意!

结语

如果以上文章对您有一点点帮助,希望您不要吝啬的点个赞加个关注,您每一次小小的举动都是我坚持写作的不懈动力!ღ( ´・ᴗ・` )

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

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

相关文章

音质更出色的骨传导耳机,设计也很时尚,南卡Runner CC3上手

这两年我几乎每天都会用耳机&#xff0c;现在耳机的种类也不少&#xff0c;如果是户外健身的话&#xff0c;我觉得骨传导耳机比挂脖式的耳机更好用&#xff0c;这种耳机因为不入耳的使用方式&#xff0c;在我们听音乐的同时&#xff0c;还可以更清晰地感知周围的情况&#xff0…

【Django项目开发】自动生成接口文档(二)

文章目录一、安装第三方库drf-yasg2二、注册到子应用INSTALLED_APPS三、配置路由1、注意四、接口文档中展示详细的文档说明视图类中接口的标准注释五、视图类中自己定义的方法实现接口文档注释1、注意一、安装第三方库drf-yasg2 pip install drf-yasg2二、注册到子应用INSTALL…

新技术不断发展,一个全新的互联网行业的新风口已然来临

拥抱实体经济&#xff0c;绝对是当下互联网玩家们的首要选择。无论是头部的互联网企业来讲&#xff0c;还是新生的互联网玩家而言&#xff0c;它们都不约而同地将关注的焦点聚焦在了这样一个方向上。   透过这一点&#xff0c;我们可以非常明显地感受到&#xff0c;一个全新的…

网络技术展开型介绍(超详细)二

♥️作者&#xff1a;小刘在这里 ♥️每天分享云计算网络运维课堂笔记&#xff0c;疫情之下&#xff0c;你我素未谋面&#xff0c;但你一定要平平安安&#xff0c;一 起努力&#xff0c;共赴美好人生&#xff01; ♥️夕阳下&#xff0c;是最美的&#xff0c;绽放&#xff0c;…

MyBatis—MyBatis参数详解

文章目录MyBatis参数详解1、 parameterType2、resultType3、 resultMap结果类型MyBatis获取参数值的两种方式1.配置sql输出日志2.看#{}和${}的输出现象3.#{}的预编译4.sql注入5.什么时候使用${}6.如何选择使用 #{} 和 ${}7.为什么#{}可以预防sql注入—————————————…

Shuffle的作用以及MapReduce的Shuffle过程

Shuffle的设计 为什么需要Shuffle&#xff1f; Shuffle的本质是基于磁盘划分来解决分布式大数据量的全局分组、全局排序、重新分区【增大】 等问题 因为单台机器的资源处理不了分布式大数据量全局分区/排序/分组 所以需要通过Shuffle对每一台机器的数据构建一个Task来做分区的…

通关剑指 Offer——剑指 Offer II 055. 二叉搜索树迭代器

1.题目描述 剑指 Offer II 055. 二叉搜索树迭代器 实现一个二叉搜索树迭代器类BSTIterator &#xff0c;表示一个按中序遍历二叉搜索树&#xff08;BST&#xff09;的迭代器&#xff1a; BSTIterator(TreeNode root) 初始化 BSTIterator 类的一个对象。BST 的根节点 root 会…

20221221英语学习

今日新词 define v.下定义&#xff1b;确切说明&#xff08;或解释&#xff09;&#xff1b;树立&#xff0c;表明 chemistry n.化学, 物质的化学组成&#xff08;或性质&#xff09; fly v.飞, 飞翔, 航行, &#xff08;乘航空器或航天器&#xff09;航行 forfeit v.&…

港华燃气上线WeOps推动运维效能提升,托举业务智慧运行!

“嘉为蓝鲸WeOps一体化运维平台有效提升了运维工作效率&#xff0c;满足系统安全要求&#xff0c;尤其是补丁安装、巡检自动化、监控告警等功能&#xff0c;为我们节省了运维人力成本和时间成本。” ——来自港华燃气 港华集团介绍&#xff1a; 港华集团为香港中华煤气在内地…

m在LTE-A系统载波聚合下的资源分配算法的matlab仿真

目录 1.算法描述 2.仿真效果预览 3.MATLAB核心程序 4.完整MATLAB 1.算法描述 载波聚合即CA&#xff0c;是LTE-A中的关键技术。是为满足用户峰值速率和系统容量提升的要求&#xff0c;增加系统传输带宽的技术&#xff0c;通过CA技术&#xff0c;用户最高上网速率可提升到300…

IPv4 和 IPv6 报文格式详解

文章目录1 概述2 报文格式2.1 IPv42.2 IPv62.3 两者区别3 网工软考真题1 概述 2 报文格式 2.1 IPv4 中文名英文名长度 bit&#xff08;位&#xff09;解释版本Version4IP协议版本号&#xff0c;固定为 4首部长度Header Length4数据报首部的总长度。 以 4 Byte&#xff08;字节…

【Java基础知识复盘】Java概述篇

本人知识复盘系列的博客并非全部原创&#xff0c;大部分摘自网络&#xff0c;只是为了记录在自己的博客方便查阅&#xff0c;往后也会陆续在本篇博客更新本人查阅到的新的知识点&#xff0c;望悉知&#xff01; Java概述 何为编程 编程就是让计算机为解决某个问题而使用某种程…

100多条2023年元宇宙统计数据!Hubbleverse替你整理好了!

欢迎来到Hubbleverse &#x1f30d; 关注我们 关注宇宙新鲜事 &#x1f4cc; 预计阅读时长&#xff1a;9分钟 本文仅代表作者个人观点&#xff0c;不代表平台意见&#xff0c;不构成投资建议。 元宇宙最近被评为最热门的新兴技术趋势之一。在过去的几年里&#xff0c;元宇宙…

SpringBoot整合Mybatis之Mapper接口和映射文件

一、什么是MyBatis MyBatis中文网https://mybatis.net.cn/ MyBatis 是一款优秀的持久层框架&#xff0c;它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型…

消息中间件执行原理

1 关于消息中间件 1.1 什么是消息中间件&#xff1f; 消息中间件是指在分布式系统中完成消息的发送和接收的基础软件。 消息中间件也可以称消息队列&#xff08;Message Queue / MQ&#xff09;&#xff0c;用高效可靠的消息传递机制进行与平台无关的数据交流&#xff0c;并基…

区块链技术应用展望

区块链作为一个"去中心化"的分布式账本数据库&#xff0c;能够让数据的产生、运行和应用更加公开与透明。区块链被认为是互联网之后又一大创新之举&#xff0c;是第二个互联网时代——价值互联网时代的来临&#xff0c;区块链将从基础设施层面为各行各业带来巨大的变…

力扣(LeetCode)353. 贪吃蛇(2022.12.20)

请你设计一个 贪吃蛇游戏&#xff0c;该游戏将会在一个 屏幕尺寸 宽度 x 高度 的屏幕上运行。如果你不熟悉这个游戏&#xff0c;可以 点击这里 在线试玩。 起初时&#xff0c;蛇在左上角的 (0, 0) 位置&#xff0c;身体长度为 1 个单位。 你将会被给出一个数组形式的食物位置…

C++中二叉树的递归遍历方法

在《C中二叉树的非递归遍历方法2-1》中提到&#xff0c;二叉树的遍历分为前序遍历、中序遍历、后序遍历和层序遍历四种。要遍历的二叉树如图1所示。 图1 二叉树结构 创建该二叉树的代码请参见《C中二叉树的非递归遍历方法2-1》。 1 前序遍历的递归实现 前序遍历的输出顺序是…

VC2010 的控制台程序中使用 EasyX

以下是在 VC2010 的控制台应用程序中使用 EasyX 的具体步骤&#xff1a; 1. 启动 VC2010&#xff0c;点击菜单 File -> New -> Project...&#xff0c;打开 New Project 对话框&#xff1a; 2. 选择 “Win32 Console Application” 类型的项目&#xff0c;并选择项目所在…

「驱动知识」Linux下RTC时间的读写分析

Linux下RTC时间的读写分析​ 1.1.1 系统时间与RTC时间​ Linux系统下包含两个时间&#xff1a;系统时间和RTC时间。​ 系统时间&#xff1a;是由主芯片的定时器进行维护的时间&#xff0c;一般情况下都会选择芯片上最高精度的定时器作为系统时间的定时基准&#xff0c;以避免…