java设计模式4——原型模式

你离开我真会死。 提交于 2020-02-14 11:30:07

java设计模式4——原型模式

1、写在前面

本节内容与C++语言的复制构造函数、浅拷贝、深拷贝极为相似,因此建议学习者可以先了解C++的该部分的相关知识,或者学习完本节内容后,也去了解C++的相应内容,进行比对学习。

2、原型模式介绍

原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。

3、java实现克隆的核心

1、实现一个接口(Cloneable)

2、重写一个方法(clone())

clone()方法的源码分析

protected native Object clone() throws CloneNotSupportedException;

由方法声明的放回值类型=>native,可知该方法实际上是一个C++封装好的方法,由java来进行调用,相当于C++语言的复制构造函数,但是又有所区别。

4、第一种原型模式实现(浅拷贝)

4.1、建立视频的原型类

package com.xgp.company.创建型模式.第四种_原型模式.demo1;

import java.util.Date;

/**
 * 1、实现一个接口
 * 2、重写一个方法
 */
//视频原型类
public class Video implements Cloneable {
    private String name;
    private Date createTime;

    /**
     * 重写克隆方法
     * @return
     * @throws CloneNotSupportedException
     */
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    public Video() {

    }

    public Video(String name, Date createTime) {
        this.name = name;
        this.createTime = createTime;
    }

    public String getName() {
        return name;
    }

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

    public Date getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }

    @Override
    public String toString() {
        return "Video{" +
                "name='" + name + '\'' +
                ", createTime=" + createTime +
                '}';
    }
}

4.2、建立复制的客户端类

package com.xgp.company.创建型模式.第四种_原型模式.demo1;

import java.util.Date;

/**
 * 客户端:克隆
 */
public class Client {

    public static void main(String[] args) throws CloneNotSupportedException {
        //原型对象
        Video v1 = new Video("狂神说java", new Date());
        System.out.println("v1 = " + v1);
        System.out.println("v1 = " + v1.hashCode());

        System.out.println("========================");
        //v1 克隆 v2
        Video v2 = (Video) v1.clone();
        System.out.println("v2 = " + v2);
        System.out.println("v2 = " + v2.hashCode());

    }
}

运行结果:

v1 = Video{name='狂神说java', createTime=Fri Feb 14 10:26:57 CST 2020}
v1 = 1836019240
========================
v2 = Video{name='狂神说java', createTime=Fri Feb 14 10:26:57 CST 2020}
v2 = 325040804

5、弊端

5.1、揭露弊端的代码

/**
 * 演示浅克隆弊端
 * @param args
 * @throws CloneNotSupportedException
 */
public static void main(String[] args) throws CloneNotSupportedException {
    //原型对象
    Date date = new Date();
    Video v1 = new Video("狂神说java", date);
    System.out.println("v1 = " + v1);
    System.out.println("v1 = " + v1.hashCode());

    //v1 克隆 v2
    Video v2 = (Video) v1.clone();
    System.out.println("v2 = " + v2);
    System.out.println("v2 = " + v2.hashCode());

    System.out.println("========================");

    date.setTime(22222222);
    System.out.println("v1 = " + v1);
    System.out.println("v1 = " + v1.hashCode());

    //v1 克隆 v2
    System.out.println("v2 = " + v2);
    System.out.println("v2 = " + v2.hashCode());

}

5.2、弊端代码的运行结果

v1 = Video{name='狂神说java', createTime=Fri Feb 14 10:29:02 CST 2020}
v1 = 1836019240
v2 = Video{name='狂神说java', createTime=Fri Feb 14 10:29:02 CST 2020}
v2 = 325040804
========================
v1 = Video{name='狂神说java', createTime=Thu Jan 01 14:10:22 CST 1970}
v1 = 1836019240
v2 = Video{name='狂神说java', createTime=Thu Jan 01 14:10:22 CST 1970}
v2 = 325040804

5.3、弊端分析

从运行结果可以看出,当改变一个被v1,v2都引用的时间对象时,两者都发生了改变,没有将引用的对象进行复制,因此称之为浅拷贝。

5.4、模型图如下:

通过上面的模型图可以看到,被引用的对象还是只有一份,因此存在很大的弊端。在C++中,如果存在这种引用关闭,当程序员两次删除对象时,会出现第二次删对象时发生异常的情况。虽然在java语言中,有着gc机制,不需要程序员手动的去释放对象,不会出现重复删除的错误。但是,当被引用的对象发生改变时,很有可能会发生数据上的不正确。

6、改进(深拷贝模式)

6.1、我们期望想要改进后的模型图如下:被引用的对象也复制了一份,互不干扰。

6.2、实现方式:改造重写的克隆方法

@Override
protected Object clone() throws CloneNotSupportedException {
    /**
     * 改造克隆方法,进行生克隆
     */
    Object obj = super.clone();
    Video v = (Video) obj;

    //将对象的属性进行克隆
    v.createTime = (Date) this.createTime.clone();
    return obj;
}

6.3、此时的运行结果为:

v1 = Video{name='狂神说java', createTime=Fri Feb 14 10:37:33 CST 2020}
v1 = 1836019240
v2 = Video{name='狂神说java', createTime=Fri Feb 14 10:37:33 CST 2020}
v2 = 325040804
========================
v1 = Video{name='狂神说java', createTime=Thu Jan 01 14:10:22 CST 1970}
v1 = 1836019240
v2 = Video{name='狂神说java', createTime=Fri Feb 14 10:37:33 CST 2020}
v2 = 325040804

6.4、当原型引用的对像改变时,复制的对象并不发生改变,被引用的对象被复制了两份,因此称之为深拷贝。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!