首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >设计模式——原型模式

设计模式——原型模式

作者头像
健程之道
发布于 2019-11-02 14:01:26
发布于 2019-11-02 14:01:26
4970
举报
文章被收录于专栏:健程之道健程之道

设计模式中,单例模式应该是大家最为熟悉的了,那如果我们需要对一个对象进行多次复制的话,大家会用什么呢?这就要用到今天要讲的原型模式了。

简介

其定义为:

使用原型实例指定将要创建的对象类型,通过复制这个实例创建新的对象。

具体来说就是,通过给出一个原型对象来指明所创建的对象的类型,然后使用自身实现的克隆接口来复制这个原型对象,该模式就是用这种方式来创建出更多同类型的对象。

这样的好处是:

Object 类的 clone() 方法是一个本地方法,它可以直接操作内存中的二进制流,所以性能相对 new 实例化来说,更加优秀。

一个对象通过 new 实例化创建过程为:

  1. 在内存中开辟一块空间。
  2. 在开辟的内存空间中创建对象。
  3. 调用对象的构造函数进行初始化对象。

而一个对象通过 clone() 创建过程为:

  1. 根据原对象内存大小开辟一块内存空间。
  2. 复制已有对象,克隆对象中所有属性值。

相对 new 来说,clone() 少了调用构造函数。如果构造函数中存在大量属性初始化或大对象,则使用 clone() 的复制对象的方式性能会好一些。

简单例子

让我们通过一个例子来具体了解一下:

代码语言:javascript
AI代码解释
复制
/**
 * 实现Cloneable 接口的原型抽象类Prototype
 */
public class Prototype implements Cloneable {
    /**
     * 重写 clone() 方法
     */
    @Override
    public Prototype clone() {
        Prototype prototype = null;
        try {
            prototype = (Prototype) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return prototype;
    }
}

/**
 * 实现原型类
 */
public class ConcretePrototype extends Prototype {
    public void show() {
        System.out.println("原型模式实现类");
    }
}

/**
 * 测试类
 */
public class Client {
    public static void main(String[] args) {
        ConcretePrototype cp = new ConcretePrototype();
        for (int i = 0; i < 10; i++) {
            ConcretePrototype cloneCp = (ConcretePrototype) cp.clone();
            cloneCp.show();
        }
    }
}

当我们实现原型抽象类时,需要注意三点:

  1. 实现 Cloneable 接口:Cloneable 接口与序列化接口的作用类似,它只是告诉虚拟机可以安全地在实现了这个接口的类上使用 clone() 方法。在 JVM 中,只有实现了 Cloneable 接口的类才可以被拷贝,否则会抛出 CloneNotSupportedException 异常。
  2. 重写 Object 类中的 clone() 方法:在 Java 中,所有类的父类都是 Object 类,而 Object 类中有一个 clone() 方法,作用是返回对象的一个拷贝。
  3. 在重写的 clone() 方法中调用 super.clone():默认情况下,类不具备复制对象的能力,需要调用 super.clone() 来实现。

深拷贝与浅拷贝

谈到了拷贝,就不得不说到一个经典的问题:深拷贝与浅拷贝,有的地方也叫深克隆与浅克隆

在上面的原型模式中,在调用 super.clone() 方法之后,首先会检查当前对象所属的类是否支持 clone,也就是看该类是否实现了 Cloneable 接口。

如果支持,则创建当前对象所属类的一个新对象,并对该对象进行初始化,使得新对象的成员变量的值与当前对象的成员变量的值一模一样,但对于其它对象的引用以及 List 等类型的成员属性,则只能复制这些对象的引用了。所以简单调用 super.clone() 这种克隆对象方式,就是一种浅拷贝

为了让大家更加清楚浅拷贝的弊端,举个具体的例子:

Student 类中有一个 Teacher 对象,我们让这两个类都实现 Cloneable 接口:

代码语言:javascript
AI代码解释
复制
@Getter
@Setter
public class Student implements Cloneable{

    /**
     * 学生姓名
     */
    private String name;

    /**
     * 学生所属的老师
     */
    private Teacher teacher;

    /**
     * 重写克隆方法,对学生进行克隆
     */
    public Student clone() {
        Student student = null;
        try {
            student = (Student) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return student;
    }
}

@Getter
@Setter
public class Teacher implements Cloneable{

    /**
     * 老师姓名
     */
    private String name;

    /**
     * 重写克隆方法,对老师类进行克隆
     */
    public Teacher clone() {
        Teacher teacher= null;
        try {
            teacher= (Teacher) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return teacher;
    }
}

测试的时候,我们先定义一个学生和一个老师,并让其关联在一起。然后复制之前的学生,生成一个新的学生,修改新学生的老师。

代码语言:javascript
AI代码解释
复制
public class Test {
    public static void main(String args[]) {
        // 定义老师1
        Teacher teacher = new Teacher();
        teacher.setName("刘老师");
        // 定义学生1
        Student stu1 = new Student();
        stu1.setName("test1");
        // 老师1和学生1进行关联
        stu1.setTeacher(teacher);

        // 复制学生1,生成学生2
        Student stu2 = stu1.clone();
        stu2.setName("test2");
        // 修改学生2的老师
        stu2.getTeacher().setName("王老师");

        // 查看修改结果
        System.out.println("学生" + stu1.getName() + "的老师是:" + stu1.getTeacher().getName());
        System.out.println("学生" + stu1.getName() + "的老师是:" + stu2.getTeacher().getName());
    }
}

我们想要的结果是:

代码语言:javascript
AI代码解释
复制
学生test1的老师是:刘老师
学生test2的老师是:王老师

但实际结果是:

代码语言:javascript
AI代码解释
复制
学生test1的老师是:王老师
学生test2的老师是:王老师

观察以上运行结果,我们可以发现:在我们给学生2修改老师的时候,学生1的老师也跟着被修改了。这就是浅拷贝带来的问题。

我们可以通过深拷贝的方式解决这类问题,修改 Student 类的 clone() 方法:

代码语言:javascript
AI代码解释
复制
    /**
     * 重写克隆方法,对学生和老师都进行克隆
     */
    public Student clone() {
        Student student = null;
        try {
            student = (Student) super.clone();
            // 克隆 teacher 对象
            Teacher teacher = this.teacher.clone();
            student.setTeacher(teacher);
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return student;
    }

此时,我们再次运行 Test 中的 main() 方法,就可以得到我们预想的结果了。

适用场景

在一些重复创建对象的场景下,我们就可以使用原型模式来提高对象的创建性能。例如:循环体内创建对象时,我们就可以考虑用 clone() 的方式来实现。

除此之外,原型模式在开源框架中的应用也非常广泛。例如 Spring 中,@Service 默认都是单例的。用了私有全局变量,若不想影响下次注入或每次上下文获取 bean,就需要用到原型模式,我们可以通过以下注解来实现,@Scope("prototype")。有兴趣的朋友深入了解一下其中的原理。

总结

原型模式,就是针对需要大量复制同一对象的场景,比如用户获取商品、循环体内创建对象等,都是不错的选择,且效率好。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-10-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 健程之道 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
Your Guide to NLP with MLSQL Stack (一)
MLSQL stack supports a complete pipeline of train/predict. This means the following steps can be in the same script:
用户2936994
2019/05/15
6290
Your Guide to Python with MLSQL Stack (二)
In the previous post Your Guide to NLP with MLSQL Stack (一), we already have known how to build a RandomForest model to classify text content. The TF/IDF, RandomForest are all built-in algorithms and implemented by Java. In this post, we will show you how to use Python to do the same job.
用户2936994
2019/05/14
6170
Your Guide to Python with MLSQL Stack (二)
MLSQL拥抱BigDL,轻轻松松无编码玩深度学习
原谅我,前半句是真的,后半句是噱头,但是真的很简化了。 MLSQL已经有一个相对来比较完善的Python Runtime,细节可以参看这篇文章,所以玩深度学习是很容易的,不过需要你提供一段tensorflow代码或者项目。
用户2936994
2018/10/15
5150
新一代AI平台-MLSQL ,加入开源社区吧!
MLSQL社区希望人人都能够参与进来。开源应该是普惠的,这种普惠应该是在价值的发挥上,以及社区的参与上。我们认为积极的社区参与体现在如下点:
木东居士
2020/04/26
1.1K0
新一代AI平台-MLSQL ,加入开源社区吧!
写给【工程同学】的MLSQL机器学习教程
http://qwone.com/~jason/20Newsgroups/20news-19997.tar.gz
用户2936994
2022/04/25
4280
写给【工程同学】的MLSQL机器学习教程
AI模型注册成MLSQL UDF函数示例
训练一个Tensorflow模型 下面的代码仅支持Console notebook模式下运行 首先,准备minist数据集 include lib.`github.com/allwefantasy/lib-core` where force="true" and libMirror="gitee.com" and -- proxy configuration. alias="libCore"; -- dump minist data to object storage include
用户2936994
2022/07/21
3900
如何实现语法的自解释(MLSQL易用性设计有感)
突然想明白了一件事, 语法应该是自解释的。什么意思呢,就是用户需要有一个学习语法的语法,而这个语法应该极度简单,他只要花上一分钟,甚至依靠直觉就能知道怎么用,透过这个口,以点窥面,让用户具备自主学习其他语法的能力。
用户2936994
2018/09/29
5940
如何实现语法的自解释(MLSQL易用性设计有感)
MLSQL Stack 让流调试更加简单
有一位同学正在调研MLSQL Stack对流的支持。然后说了流调试其实挺困难的。经过实践,希望实现如下三点:
用户2936994
2019/06/05
4520
MLSQL Stack 让流调试更加简单
MLSQL解决了什么问题
在谈MLSQL解决了什么问题之前,我们先提一个“数据中台”的概念。什么是数据中台呢?数据中台至少应该具备如下三个特点:
用户2936994
2018/12/28
1K0
MLSQL解决了什么问题
在谈MLSQL解决了什么问题之前,我们先提一个“数据中台”的概念。什么是数据中台呢?数据中台至少应该具备如下三个特点:
木东居士
2020/04/26
8620
MLSQL解决了什么问题
用MLSQL完成简书图片备份
我今天正好想做两个事,第一个是,我想把我简书内容备份下来,但是官方提供的备份功能只能备份成markdown,然后发现图片没办法备份。所以我需要把我简书里的所有图片下载下来。
用户2936994
2019/04/01
5320
用MLSQL完成简书图片备份
算法训练和模型部署如何避免多次重写数据预处理代码
前段时间,我们对接算法的工程师哭丧的和我说,模型生成后一般都要部署成API的形态对外提供服务,但是算法工程师并没有提供如何将一条数据转化特征向量的方法,他能拿到的是代码逻辑以及一些“中间元数据”。数据预处理本来就复杂,翻译也是一件极其困难的事情。我解释了这件事情难以解决的原因,但是显然他还是有些失望。
用户1332428
2018/07/30
9160
MLSQL解决了什么问题
1、项目难以重现,可阅读性和环境要求导致能把另外一个同事写的python项目运行起来不得不靠运气
用户1332428
2018/07/30
8990
MLSQL-ET开发指南
MLSQL具备足够灵活的扩展性,能够同时解决 Data + AI 领域的问题。我们提供了大量的插件,方便用户在数据处理、商业分析和机器学习的不同场景中使用 MLSQL。这些插件类型包括: DataSource、ET、Script、App,我们都可以灵活的通过离线或者线上的方式注册到 MLSQL Engine 中使用。 在 MLSQL 中,ET(Estimator/Transformer的简称)是一个非常重要的概念。通过 ET,我们可以完成非常多的复杂任务。包括:
用户2936994
2022/01/07
8890
MLSQL-ET开发指南
为什么去开发一个MLSQL
第一个,算法的着眼点是,用最快速的方式清洗一些数据出来,然后接着建模训练,评估预测效果,之后再重复清洗数据,再试验。因为很多算法工程师都是Python系的,对他们来说,最简单的方式自然是写python程序。一旦确认清洗方式后,这种数据清洗工作,最后研发工程师还要再重新用Spark去实现一遍。那么如果让算法工程师在做数据清洗的时候,直接使用PySpark呢?这样复用程度是不是可以有所提高?实际上是有的。但是算法工程师初期用起来会比较吃力,因为PySpark的学习成本还是有的,而且不小。
用户2936994
2018/08/27
7620
为什么去开发一个MLSQL
谷歌BigQuery ML VS StreamingPro MLSQL
今天看到了一篇 AI前线的文章谷歌BigQuery ML正式上岗,只会用SQL也能玩转机器学习!。正好自己也在力推 StreamingPro的MLSQL。 今天就来对比下这两款产品。
用户2936994
2018/08/27
1.8K0
【1】paddle飞桨框架高层API使用讲解
飞桨框架2.0全新推出高层API,是对飞桨API的进一步封装与升级,提供了更加简洁易用的API,进一步提升了飞桨的易学易用性,并增强飞桨的功能。
汀丶人工智能
2022/12/21
9930
【1】paddle飞桨框架高层API使用讲解
手把手教你R语言随机森林使用
随机森林是常用的非线性用于构建分类器的算法,它是由数目众多的弱决策树构建成森林进而对结果进行投票判断标签的方法。
生信学习者
2024/06/11
1.2K0
手把手教你R语言随机森林使用
MLSQL 对Python的支持之路
Python是做机器学习框架一定要支持的。MLSQL很早就支持集成Python脚本做模型的训练和预测。
用户2936994
2018/10/11
8730
Byzer + OpenMLDB 实现端到端的,基于实时特征计算的机器学习流程
本文示范如何使用OpenMLDB和 Byzer-lang 联合完成一个完整的机器学习应用。Byzer-lang 作为面向大数据和AI的一门语言,通过 Byzer-Notebook 和用户进行交互,用户可以轻松完成数据的抽取,ETL,特征/模型训练,保存,部署到最后预测等整个端到端的机器学习流程。OpenMLDB在本例中接收Byzer发送的指令和数据,完成数据的实时特征计算,并经特征工程处理后的数据集返回Byzer,供其进行后续的机器学习训练和预测。
用户2936994
2022/10/27
1.2K0
相关推荐
Your Guide to NLP with MLSQL Stack (一)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
首页
学习
活动
专区
圈层
工具
MCP广场
首页
学习
活动
专区
圈层
工具
MCP广场