原创不如山寨——原型模式详解

1. 前言

现实世界中山寨这种行为往往意味着假冒伪劣,备受批判。但是在软件开发中,山寨却又不少可取之处。首先其“成分”和“质量”和原创不相上下;其次相比原创一个东西的时间开销,山寨一个出来总归是省时省力的,毕竟对于计算机,克隆一个对象要比创建一个对象性能好得多(拷贝对象不会执行构造方法)。如果在开发中我们需要一个类的多个实例,这些实例只在某些属性细节上不同,相比直接new出它们的时间开销,从一个实例原型拷贝出其他的实例或许是更可取的方法。而原型模式,或者说克隆模式就能够帮我们做到这点。

2. 原型模式详解

2.1 原型模式详解

用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

2.2 原型模式类结构

原型模式中的主要角色是Prototype类,含有clone方法供客户端调用,从而创建它的拷贝对象。类结构简单清晰。

2.3 原型模式的代码实现

  • 首先创建原型类,其实现了Cloneable接口,并重写自Object继承的clone方法。
public class Prototype implements Cloneable {

    @Override
    protected Prototype clone() throws CloneNotSupportedException {
        return(Prototype)super.clone();
    }
}
  • 测试
public class TestCase {

    public static void main(String[] args) throws CloneNotSupportedException {

        Prototype pObj = new Prototype();

        Prototype cObj = pObj.clone();

        System.out.println(pObj);

        System.out.println(cObj);

    }
}

首先创建了一个原型类的实例,其次调用该实例的clone方法获得了该实例的拷贝,分别打印出两个对象的哈希值

  • 结果

3. 关于对象拷贝的深拷贝和浅拷贝问题

3.1 什么是深拷贝和浅拷贝

  • 浅拷贝

    上面我们通过实现标记接口Clone使得该对象具有创建一个和自己一摸一样的拷贝对象的能力,然而这种拷贝是浅拷贝,与之相对的是深拷贝。关于浅拷贝和深拷贝,可以见下图

当原对象内部有一个非基本类型的引用变量时,浅拷贝意味着拷贝对象内部该变量和原对象内部的引用变量指向同一个对象,通俗的说,它只对外部对象进行拷贝,对内部引用变量指向的对象不进行真实拷贝,只是指向该对象而已。

  • 深拷贝

深拷贝,除了拷贝外部对象外,其引用变量指向的对象也要拷贝,同时如果该对象还有引用变量,其指向的对象同样需要拷贝,一直递归进行指导拷贝完成。可以看出,深拷贝是深度的拷贝。如果对象间的组合关系十分复杂的话,深度拷贝过程就类似于树的遍历了。

3.2 Java的Clone接口实现的是浅拷贝

对此,我们可以验证如下,首先修改原型类,添加一个引用变量obj指向Object对象。

public class Prototype implements Cloneable {

    public Object obj=new Object();

    @Override
    protected Prototype clone() throws CloneNotSupportedException {
        return(Prototype)super.clone();
    }
}

测试比较原型对象和拷贝对象的obj变量是否指向同一Object对象

public class TestCase {

    public static void main(String[] args) throws CloneNotSupportedException {

        Prototype pObj = new Prototype();

        Prototype cObj = pObj.clone();

        System.out.println(pObj.obj.equals(cObj.obj));

    }
}

运行结果如下

3.3 使用序列化和反序列化实现深拷贝

实现深拷贝的方式很多,比如递归实现浅拷贝,或者工作中可以使用开源类库来做。下面介绍一种使用对象序列化和反序列化实现深拷贝的方式。原型类必须实现另一个标记接口Serializable,且其内部引用变量指向的对象也必须实现该序列化接口,由于Object没有实现该接口,所以会报出NotSerializableException异常。下面给出关键的序列化和反序列化代码,就不修改原型类进行测试了。

import java.io.*;

public class TestCase {

    public static void main(String[] args) throws CloneNotSupportedException, IOException, ClassNotFoundException {

        Prototype pObj = new Prototype();

        //序列化
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(pObj);

        byte[] bytes = bos.toByteArray();

        //反序列化
        ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
        ObjectInputStream ois = new ObjectInputStream(bis);
        Prototype cObj = (Prototype) ois.readObject();
        System.out.println(pObj.obj.equals(cObj.obj));

    }
}

总结

原型模式通过对象拷贝的方式获得类的多个实例。在很多情况下这样能获得更好的性能。原型模式通常和其他模式结合使用,比如Spring框架中通过和工厂模式结合使得我们可以获得相同Bean的多个实例,这样帮我们我们屏蔽了创建新实例的复杂细节。

原文地址:https://www.cnblogs.com/takumicx/p/9280926.html

时间: 2024-10-06 21:51:31

原创不如山寨——原型模式详解的相关文章

Javascript设计模式之装饰者模式详解篇

一.前言: 装饰者模式(Decorator Pattern):在不改变原类和继承的情况下动态扩展对象功能,通过包装一个对象来实现一个新的具有原对象相同接口的新的对象. 装饰者模式的特点:1. 在不改变原对象的原本结构的情况下进行功能添加.2. 装饰对象和原对象具有相同的接口,可以使客户以与原对象相同的方式使用装饰对象.3. 装饰对象中包含原对象的引用,即装饰对象是真正的原对象经过包装后的对象. 二.Javascript装饰者模式详解: 描述:装饰者模式中,可以在运行时动态添加附加功能到对象中.当

Extjs MVC开发模式详解

Extjs MVC开发模式详解 在JS的开发过程中,大规模的JS脚本难以组织和维护,这一直是困扰前端开发人员的头等问题.Extjs为了解决这种问题,在Extjs 4.x版本中引入了MVC开发模式,开始将一个JS(Extjs)应用程序分割成Model-View-Controller三层,为JS应用程序的如何组织代码指明了方向,同时使得大规模JS代码变得更加易于重用和维护:这就是Extjs MVC开发模式的初衷. 在官方给出的MVC例子中,我们可以看到一个简单的列表编辑功能,这篇文章就围绕这个功能进

二、设计模式总览及工厂模式详解

二.架构师内功心法之设计模式 2.架构师内功心法之设计模式 2.1.课程目标 1.通过对本章内容的学习,了解设计模式的由来. 2.介绍设计模式能帮我们解决哪些问题. 3.剖析工厂模式的历史由来及应用场景. 2.2.内容定位 不用设计模式并非不可以,但是用好设计模式能帮助我们更好地解决实际问题,设计模式最重要的 是解耦.设计模式天天都在用,但自己却无感知.我们把设计模式作为一个专题,主要是学习设计模式 是如何总结经验的,把经验为自己所用.学设计模式也是锻炼将业务需求转换技术实现的一种非常有效 的方

Javascript 严格模式详解

Javascript 严格模式详解 作者: 阮一峰 日期: 2013年1月14日 一.概述 除了正常运行模式,ECMAscript 5添加了第二种运行模式:"严格模式"(strict mode).顾名思义,这种模式使得Javascript在更严格的条件下运行. 设立"严格模式"的目的,主要有以下几个: - 消除Javascript语法的一些不合理.不严谨之处,减少一些怪异行为; - 消除代码运行的一些不安全之处,保证代码运行的安全: - 提高编译器效率,增加运行速度

Spartan6系列之芯片配置模式详解

1.   配置概述 Spartan6系列FPGA通过把应用程序数据导入芯片内部存储器完成芯片的配置.Spart-6 FPGA可以自己从外部非易失性存储器导入编程数据,或者通过外界的微处理器.DSP等对其进行编程.对以上任何一种情况,都有串行配置和并行配置之分,串行配置可以减少芯片对引脚的要求,并行配置对8bit/16bit Flash或者微处理器来说更合适. 因为Xilinx的FPGA器件的配置数据存储在CMOS 配置锁存器内(CCL),因此Spartan6 FPGA器件上电后必须重新配置.Sp

java 代理模式详解

java 动态代理(JDK和cglib) 设计模式这东东每次看到就明白可过段时间又不能很流利的说出来,今天就用详细的比喻和实例来加深自己的理解(小弟水平不高有不对的地方希望大家能指出来). (1)代理这个词生活中有很多比如在街边卖手机卡.充公交地铁卡的小商店他们都起了代理的作用,java中的代理跟这些小店商的作用是一样的.再比如我想在淘宝上开个服装店但又没有货源怎么办,这时候我就要跟淘宝上某一卖家联系做他的代理.我跟我的商家都要卖衣服(就好比我们都继承了卖衣服的接口sellClothesInte

设计模式 - 代理模式(proxy pattern) 未使用代理模式 详解

代理模式(proxy pattern) 未使用代理模式 详解 本文地址: http://blog.csdn.net/caroline_wendy 部分代码参考: http://blog.csdn.net/caroline_wendy/article/details/37698747 如果需要监控(monitor)类的某些状态, 则需要编写一个监控类, 并同过监控类进行监控. 但仅仅局限于本地, 如果需要远程监控, 则需要使用代理模式(proxy pattern). 具体方法: 1. 类中需要提供

保护模式详解

在ia32下,cpu有两种工作模式:实模式和保护模式. 在实模式下,16位的寄存器用"段+偏移"的方法计算有效地址. 段寄存器始终是16位的.在实模式下,段值xxxxh表示的以xxxx0h开始的一段内存.但在保护模式下,段寄存器的值变成了一个索引(还有附加信息)这个索引指向了一个数据结构的表(gdt/ldt)项,表项(描述符)中详细定义了段的其实地址.界限.属性等内容. 保护模式需要理解:描述符,选择子 描述符包括,存储段描述符(代码段,数据段,堆栈段),系统描述符(任务状态段TSS,

DES加密模式详解

DES加密模式详解 http://www.cnblogs.com/Lawson/archive/2012/05/20/2510781.html http://www.blogjava.net/wayne/archive/2011/05/23/350879.html 加密算法常见的有ECB模式和CBC模式: ECB模式:电子密本方式,这是JAVA封装的DES算法的默认模式,就是将数据按照8个字节一段进行DES加密或解密得到一段8个字节的密文或者明文,最后一段不足8个字节,则补足8个字节(注意:这里