请登录

Github帐号登录
博主提供技术支持服务,项目代做等服务QQ975532442
  • 首页
  • 博客
  • 算法
  • 前端
  • Linux
  • 数据库
  • 后端
  • 标签
  • 友情链接
  • 登录
  • 注册
1721°
牧码人
发表于: 2019-07-27 14:46

23个设计模式之原型模式

本篇首发于牧码人博客转载请加上此标示。

序言:

继续构建型的模式的学习,今天我们来讲讲原型模式(Prototype)的学习。原型模式的本质是通过复制一个样本来创建一个新对象。简单直接一点就是克隆。

但是克隆一个对象时,这时我们需要思考一个问题。我们克隆的对象是共享里面的属性值。还是继承里面的属性值。作为一个开发者,这一点是需要我们思考与注意的。所以这边便引出一个概念浅复制,与深复制。

什么是浅复制,深复制?

熟悉java的朋友都知道,java自带一个克隆方法(clone)。这是一个native的方法,作为一名系统开发者。可以找一些资料去了解一下这个方法。我这边只能告诉你结论:这个方法不做任何改变的话默认是浅复制的。

  • 浅复制

被复制对象的基本数据类型的值跟原来相同,引用对象类型的还是指向原对象的。

  • 深复制

被复制对象的基本数据类型的值跟原来相同,引用对象类型的是指向新对象的。

浅复制实践

数据类person

/**
 * @Auther: Jan 橙寂
 * @Date: 2019-7-27 11:02
 * @Description: 原型模式的工具类  需继承 Cloneable 接口
 * @Version: 1.0
 */
public class Person implements Cloneable,Serializable {


    private int age;
    private String name;

    private Address address;

    public Person(int age, String name, Address address) {
        this.age = age;
        this.name = name;
        this.address = address;
    }


    /**
     * @return 这种方式是浅复制
     * @throws CloneNotSupportedException
     */
    public Person copy() throws CloneNotSupportedException {

        return (Person) super.clone();
    }
    }

数据类address

public class Address implements Cloneable,Serializable {

    private  String name;

    public String getName() {
        return name;
    }

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

    public Address(String name)
    {
        this.name=name;
    }


    public Address copy() throws CloneNotSupportedException {
       return (Address) super.clone();
    }
    }

测试

/**
 * @Auther: Jan 橙寂
 * @Date: 2019-7-27 11:27
 * @Description:
 * @Version: 1.0
 */
public class Test {

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

        Address address = new Address("北京");
        Person p = new Person(19, "李四", address);


        //浅复制测试

        System.out.println("浅复制测试");


        Person p1 = p.copy();


        System.out.println(p == p1);


        p1.getAddress().setName("深圳");
        System.out.println(p);

        System.out.println(p1);
        }

浅复制测试

false

Person{age=19, name='李四', address=Address{name='深圳'}}

Person{age=19, name='李四', address=Address{name='深圳'}}

结论:java默认的clone方法是浅复制,复制后的对象address跟原来的对象指向是一样的。这时大家心里可能会有疑问,如果要使用java默认的clone方法实现深复制。实现深复制的话这边需要改一下原来方法的逻辑,代码如下

使用原生的clone方法实现深复制

 /**
     * @return 使用原生clone方式实现的深复制
     * @throws CloneNotSupportedException
     */
    public Person deepCopy() throws CloneNotSupportedException {

        Person p = (Person) super.clone();
        p.setAddress(p.getAddress().copy());
        return p;
    }
/**
 * @Auther: Jan 橙寂
 * @Date: 2019-7-27 11:27
 * @Description:
 * @Version: 1.0
 */
public class Test {

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

        Address address = new Address("深圳");
        Person p = new Person(19, "李四", address);


         //深复制实现clone的方式
        System.out.println("深复制测试");
        Person p2 = p.deepCopy();

        System.out.println(p == p2);

        p2.getAddress().setName("北京");
        System.out.println(p);
        System.out.println(p2);
        }

根据我们测试用例在来跑一次

深复制测试 false Person{age=19, name='李四', address=Address{name='深圳'}} Person{age=19, name='李四', address=Address{name='北京'}}

深复制总结:

① 如果有一个非原生成员,如自定义对象的成员,那么就需要: 该成员实现Cloneable接口并覆盖clone()方法,不要忘记提升为public可见。 同时,修改被复制类的clone()方法,增加成员的克隆逻辑。

② 如果被复制对象不是直接继承Object,中间还有其它继承层次,每一层super类都需要实现Cloneable接口并覆盖clone()方法。

与对象成员不同,继承关系中的clone不需要被复制类的clone()做多余的工作。

一句话来说,如果实现完整的深拷贝,需要被复制对象的继承链、引用链上的每一个对象都实现克隆机制。

前面的实例还可以接受,如果有N个对象成员,有M层继承关系,就会很麻烦。

使用序列化的方式实现深复制

这个方式可以理解为,把对象转化成二进制流的形式,然后再把流序列化成对象。 具体实现方法

 /**
     * @return 使用读二进制形式流的方式实现深克隆
     * @throws CloneNotSupportedException
     */
    public Person deepCopy1() {

        /* 写入当前对象的二进制流 */
        ByteArrayOutputStream bos = null;
        ObjectOutputStream oos = null;
        ByteArrayInputStream bis = null;
        ObjectInputStream ois = null;
        try {
            bos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(bos);
            oos.writeObject(this);
            bis = new ByteArrayInputStream(bos.toByteArray());
            ois = new ObjectInputStream(bis);
            return (Person) ois.readObject();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        /* 读出二进制流产生的新对象 */
        return null;
    }

测试

 //深复制实现流的方式
        System.out.println("深复制测试二进制流");
        Person p3 = p.deepCopy1();

        System.out.println(p == p3);

        p3.getAddress().setName("上海");
        System.out.println(p);
        System.out.println(p3);

深复制测试二进制流 false Person{age=19, name='李四', address=Address{name='深圳'}} Person{age=19, name='李四', address=Address{name='上海'}}

序列化总结: 这种方式被复制对象的继承链、引用链上的每一个对象都实现java.io.Serializable接口。这个比较简单,不需要实现任何方法,serialVersionID的要求不强制,对深拷贝来说没毛病。这种是最常用,而且比较简单的方法。

总结

所谓原型模式,就是通过复制对象来创建对象。与通过使用构造函数来比,就是通过复制的对象带有原来对象的一些状态。尤其是在一些场景对象只存在细微差别。这时就可以使用原型模式去创建对象。

源码下载github


注意:本文归作者所有,未经作者允许,不得转载

点赞 0
#设计模式 #java
阅读全部

已有 0 条评论

    我有话说: @

    热门文章

    1. java根据模板导出pdf(带源码)

      5907 阅读
    2. webSocket部署到远程服务器连接不上

      4665 阅读
    3. 本站看点

      4510 阅读
    4. 史上最详细linux上安装mysql(centos7)

      3946 阅读
    5. 23种设计模式合集(学习笔记)

      3845 阅读
    6. 记一次Git fork的经历

      3498 阅读
    7. 1.服务的注册与治理(eureka)

      3297 阅读
    8. 2018第九届届蓝桥杯国赛回顾(真题解析)

      3289 阅读
    Copyright © 牧码人 赣ICP备2022000459号