23种设计模式--------原型设计模式(创建型设计模式)

本文深入探讨了浅拷贝和深拷贝的概念及其在Java中的实现方式,包括它们的优缺点及应用场景。通过实例演示了如何实现这两种拷贝,并展示了在实际编程中如何选择合适的拷贝方式。

1.模式描述

用原型实例指定创建对象的种类,并且通过拷贝来创建新的对象。他分为浅克隆和深克隆(也可以叫拷贝)

1.浅拷贝

  什么是浅拷贝?浅拷贝后的结果是对象的内存地址变化了(对象的引用发生了变化),可对象中包含的对象内存则没有变化。只能克隆对象本身,对于依附于对象的对象则不予克隆,只对其地址克隆(这句很关键.)

浅克隆需要注意的地方:

  (1)被克隆的对象需要实现需要实现Cloneable;

  (2)要重写clone() 方法;

  (3)对于依附于被克隆对象的对象,只克隆其地址。一旦更改都会改变。


2.深拷贝

  上面说到对象的浅拷贝,在拷贝中我们对可变对象的操作有一些棘手。而深拷贝则可以解决这个问题。深拷贝的实现要依赖与对象的持久化操作。更多关于对象持久化的内容可参见本人另一篇博客:《网络爬虫:基于对象持久化实现爬虫现场快速还原》。深克隆利用了IO流进行存储,然后进行读取,不仅对克隆对象进行克隆

  对象持久化是说把一个对象写到文件中(或是向网络,或是向进程之间进行传输),当我们需要拷贝一个对象时,先把此对象固化到文件,再从文件中读取对象。这样一个过程就完成了对象的深拷贝,在博客《网络爬虫:基于对象持久化实现爬虫现场快速还原》中我也有详细地说明,这里就不再赘述了。

深克隆,无论原型对象的成员变量是值类型还是引用类型,都将复制一份给克隆对象。

  深克隆需要注意的地方:

  (1)利用IO流进行存储,然后进行读取;

  (2)需要实现可序列化接口Serializable。

 

  优点

  (1)当创建一个结构复杂的对象时,原型模式可以简化其创建过程,通过复制已有的实例来获得相同的对象;

  (2)简化创建结构,不需要专门的工厂存在,仅需要实现克隆方法即可。

  缺点

  (1)必须实现Cloneable接口;

  (2)逃避构造函数的约束;

  (3)克隆方法在类的内部,加重类的职责,当需要对已有类进行改造时需要修改原型类。


浅拷贝和深拷贝的区别;通俗一点讲,:复制地址内存地址的叫浅拷贝,复制东西的叫深拷贝 .  

     拷贝的对象和原来对象地址值相同(都指向堆中同一个对象地址),就是浅拷贝, 如果拷贝的对象和原对象,地址值不同(内存堆中,不同的对象地址),就是深拷贝!(理解错误)


2.模式作用

可以一定程度上解耦,消费者和对象的构造过程隔离开,对象如何构造与消费者完全无关。

可以一定程度提升效率,复杂对象的构造往往需要较长的时间(中间可能会进行复杂运算或者数据库交互),clone消耗的资源一般情况下会少很多。
可以一定程度增加代码的封装性,避免复杂的构造过程。

等等。

3.适用场景

需要创建一个复杂的、构造耗时很多的对象,且已有一个同类对象的时候。

消费者不关心对象构造过程的时候。

等等。

例如:工作流实例的重建、复杂数据实体的复制

4.模式要素

该类要支持克隆。该类要实现Cloneable接口。

已有该类的一个实例该实例作为原型进行clone。

该类的克隆方法应为深克隆。该类中除了8中基本类型(以及他们的封装类)、String(其实是浅clone,但是了解一下String就会发现其实结果和深clone一致,这里我们认为就是深clone)之外的属性均需要进行深clone;或者采用流化clone方式进行深clone。

本文借鉴于;http://blog.youkuaiyun.com/zhangjg_blog/article/details/18369201 和 http://blog.youkuaiyun.com/chenliguan/article/details/69855738 和https://www.cnblogs.com/chenxkang/p/6696228.html

  

4.1 优点

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

(2)还有一个重要的用途就是保护性拷贝,也就是对某个对象对外可能是只读的,为了防止外部对这个只读对象的修改,通常可以通过返回一个对象拷贝的形式实现只读的限制。

曾经有人进行过数据优化测试,采用原型模式和new 对象出来,测试结果如下;

原型模式构建对象和 new 对象效率比较

采用原型模式创建对象200次耗时:0

采用new创建对象200次耗时:2057

所以,通过原型模式创建对象,可以大大提高创建的效率,直接克隆,避免了重新执行构造过程。原型模式和工厂模式搭配起来,是常用的使用方式。



4.2 缺点

(1)这既是它的优点也是缺点,直接在内存中拷贝,构造函数是不会执行的,在实际开发中应该注意这个潜在问题。优点是减少了约束,缺点也是减少了约束,需要大家在实际应用时考虑。 
(2)通过实现Cloneable接口的原型模式在调用clone函数构造实例时并不一定比通过new操作速度快,只有当通过new构造对象较为耗时或者说成本较高时,通过clone方法才能够获得效率上的提升。


代码;

A.浅拷贝:

/**
 * Created by dwb on 2018/3/12.
 * describe1: 创建型设计模式之 原型设计模式
 * describe2: 第一步,创建一个基本类,并实现Cloneable接口,重写Clone的方法
 * email:wolfking0608@163.com
 */

public class Sender implements Cloneable {
    private int age;//基本数据类型
    private String name;//引用数据类型

    public Sender(int age, String name) {
        this.age = age;
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

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

2.测试

/**
 * Created by dwb on 2018/3/14.
 * describe1:原型设计模式, 浅拷贝测试
 * describe2:
 * email:wolfking0608@163.com
 */

public class Test {

    public static void main(String[] args) throws CloneNotSupportedException {
        Sender sender = new Sender(11, "king");
        Sender cloneSender = (Sender) sender.clone();
        String name = sender.getName();
        String cloneName = cloneSender.getName();
        if (name == cloneName) {//这里如果换成是两个对象进行对比,结果却是不相等,对象不是存储在堆中吗?为啥地址值不同呢?目前先放在这里.有知道的请留言
            System.out.println("clone属于浅拷贝");
        } else {
            System.out.println("属于深拷贝");
        }
    }
}

 运行结果; 

clone属于浅拷贝

B.深拷贝

   1.

/**
 * Created by dwb on 2018/3/12.
 * describe1: 创建型设计模式之 原型设计模式
 * describe2: 第一步,创建一个基本类,并实现Cloneable接口,重写Clone的方法
 * email:wolfking0608@163.com
 */

public  class Sender implements Cloneable {
    private int        age;//基本数据类型
    private String     name;//引用数据类型


    public Sender(int age, String name) {
        this.age = age;
        this.name = name;
    }



    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    protected Sender clone() throws CloneNotSupportedException {
        Sender sender = (Sender) super.clone();
        return sender;
    }

}

2.

/**
 * Created by dwb on 2018/3/14.
 * describe1:
 * describe2:
 * email:wolfking0608@163.com
 */

public class Prototype implements Cloneable {

    private int    id;
    private Sender mSender;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public Sender getSender() {
        return mSender;
    }

    public void setSender(Sender sender) {
        mSender = sender;
    }

    public Object clone() {
        try {
            boolean deep = true;

            if (deep) {
                /**
                 * 深复制,复制出了两个sender
                 * */
                Prototype prototype = (Prototype) super.clone();
                prototype.setSender((Sender) this.mSender.clone());
                // 继续复制其他引用对象
                return prototype;
            }else{
                /**
                 * 浅复制 ,是同一个sender
                 * */
                return super.clone();
            }
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            return null;
        }

    }


}

3.

/**
 * Created by dwb on 2018/3/14.
 * describe1:原型设计模式, 深拷贝测试
 * describe2:
 * email:wolfking0608@163.com
 */

public class Test {

    public static void main(String[] args) throws CloneNotSupportedException {
        Prototype p1 = new Prototype();
        p1.setSender(new Sender(11, "king"));
        p1.setId(1);
        // 复制
        Prototype p2 = (Prototype) p1.clone();
        p2.setId(2);
        System.out.println(p1 == p2 ? "p1和p2是浅克隆" : "p1和p2是深克隆");
        System.out.println(p1.getSender()==p2.getSender()?"p1的sender和p2的sender地址一样":"p1的sender和p2的sender地址不同");
    }
}

运行结果:

  1. deep为true时;     

p1和p2是深克隆
p1的sender和p2的sender地址不同

  2.deep为false时

p1和p2是深克隆
p1的sender和p2的sender地址一样


最后安卓开发中,对于该原型模式,用到的概率非常非常小.所以你可以先了解下.....



评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值