23种设计模式(5)-原型模式

一, 简介

当系统中需要大量创建相同或者相似的对象时,就可以通过“原型模式”来实现。原型模式也是“创建型设计模式”中的一种,五种创建型设计模式到此就介绍完毕了,需要了解前四种的可以看看本人前面的文章。

原型模式的核心思想是,通过拷贝指定的“原型实例(对象)”,创建跟该对象一样的新对象。简单理解就是“克隆指定对象”。

这里提到的“原型实例(对象)”,就是被克隆的对象,它的作用就是指定要创建的对象种类。

二,实现

所有的原型模式都是实现CloneAble接口,这接口是一个标记接口,里面没有任何内容,作用就是用于标识实现该接口 的启用了原型模式。只有当实现了CloneAble接口以后才有重写Object类clone方法的权限,否则会报CloneNotSupportedException异常。
在这里插入图片描述

三,浅拷贝和深拷贝

原型模式有两种,一种是浅拷贝另一种是深拷贝。

浅拷贝:当类的成员变量是基本数据类型时,原对象的值会直接赋给新的对象,当成员变量为引用数据类型时,克隆对象会引用原有的引用地址,当原对象引用地址的变量被修改时,克隆对象中的变量也会跟着改变。

深拷贝:深拷贝则是重新开拓出一块新的内存地址,原对象的引用地址的变量发生改变时对克隆对象无影响。其余跟浅拷贝一致。

四,实现

浅拷贝实现:

package clone;

/**
 * @author: chenwj
 * @description:
 * @create: 2021-02-18 17:26
 **/
public class Person implements Cloneable{
    String name;

    Integer age;

    Integer stature;

    Skills skills;

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

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

    public Integer getStature() {
        return stature;
    }

    public void setStature(Integer stature) {
        this.stature = stature;
    }

    public Skills getSkills() {
        return skills;
    }

    public void setSkills(Skills skills) {
        this.skills = skills;
    }

    public Person(String name, Integer age, Integer stature,Skills skills) {
        this.name = name;
        this.age = age;
        this.stature = stature;
        this.skills = skills;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {

        return super.clone();
    }
}

class Main{
    public static void main(String[] args) throws CloneNotSupportedException {
        Skills skills = new Skills();
        skills.setEat("eat");
        skills.setSleep("1ms");
        Person person = new Person("vangi",25,180,skills);
        Person person1 = (Person) person.clone();
        System.out.println(person.getName()+"-"+person.getAge()+"-"+person.getStature()+"-"+person.getSkills().getSleep());
        System.out.println(person1.getName()+"-"+person1.getAge()+"-"+person1.getStature()+"-"+person.getSkills().getSleep());
        skills.setSleep("2ms");
        System.out.println("原对象:"+person.getName()+"-"+person.getAge()+"-"+person.getStature()+"-"+person.getSkills().getSleep());
        System.out.println("克隆对象"+person1.getName()+"-"+person1.getAge()+"-"+person1.getStature()+"-"+person.getSkills().getSleep());
    }
}
}


这里我将skill中的字段sleep的值进行了修改,可以发现原对象个克隆对象都发生了改变。
要注意的是String是通过常量赋值,相当于基本数据类型,当用new时则为引用数据类型。

深拷贝实现:
深拷贝实现由两种方式:一是自行开辟出一块内存地址,并把克隆的对象赋给它,二是通过序列化的方式实现(推荐)。
首先说第一种方式:
我们先来建一个引用类型对象Skills:

package clone;

/**
 * @author: chenwj
 * @description:
 * @create: 2021-02-18 17:56
 **/
public class Skills implements Cloneable{
    String eat;
    String sleep;

    public String getEat() {
        return eat;
    }

    public void setEat(String eat) {
        this.eat = eat;
    }

    public String getSleep() {
        return sleep;
    }

    public void setSleep(String sleep) {
        this.sleep = sleep;
    }

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

Person类:

package clone;

/**
 * @author: chenwj
 * @description:
 * @create: 2021-02-18 17:26
 **/
public class Person implements Cloneable{
    String name;

    Integer age;

    Integer stature;

    Skills skills;

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

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

    public Integer getStature() {
        return stature;
    }

    public void setStature(Integer stature) {
        this.stature = stature;
    }

    public Skills getSkills() {
        return skills;
    }

    public void setSkills(Skills skills) {
        this.skills = skills;
    }

    public Person(String name, Integer age, Integer stature,Skills skills) {
        this.name = name;
        this.age = age;
        this.stature = stature;
        this.skills = skills;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Person person = null;
        person = (Person) super.clone();
        person.skills = (Skills) this.skills.clone();
        return person;
    }
}

class Main{
    public static void main(String[] args) throws CloneNotSupportedException {
        Skills skills = new Skills();
        skills.setEat("eat");
        skills.setSleep("1ms");
        Person person = new Person("vangi",25,180,skills);
        Person person1 = (Person) person.clone();
        System.out.println("原对象:"+person.getName()+"-"+person.getAge()+"-"+person.getStature()+"-"+person.getSkills().getSleep());
        System.out.println("克隆对象:"+person1.getName()+"-"+person1.getAge()+"-"+person1.getStature()+"-"+person1.getSkills().getSleep());
        skills.setSleep("2ms");
        System.out.println("修改后原对象:"+person.getName()+"-"+person.getAge()+"-"+person.getStature()+"-"+person.getSkills().getSleep());
        System.out.println("修改后克隆对象:"+person1.getName()+"-"+person1.getAge()+"-"+person1.getStature()+"-"+person1.getSkills().getSleep());
    }
}

此时由于重新给克隆对象分配了一块内存,所以克隆对象的值并没有发生改变。
在这里插入图片描述
这种方法的缺陷是后续所有的引用数据类型都需要在clone方法中单独处理,违反了开闭原则。

第二种方式,通过序列化

public Person deepClone(){
       ObjectInputStream ois = null;
       ObjectOutputStream oos = null;
       ByteArrayInputStream bis = null;
       ByteArrayOutputStream bos = 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 | ClassNotFoundException e) {
           e.printStackTrace();
       } finally {
           try {
               ois.close();
               oos.close();
               bis.close();
               bos.close();
           } catch (IOException e) {
               e.printStackTrace();
           }
       }
       return null;
   }

这样就不用每次在clone方法中对引用数据类型做处理了。
需要注意的是使用序列化时记得给每个类实现Serializable接口,不然会报NotSerializableException异常。

总结:
1、通过原型模式可以简化创建重量级对象的过程,并提高程序的效率。
2、原型设计模式是动态获取对象运行时的状态进行创建对象的。
3、使用原型设计模式可以使代码变的更加灵活,因为当原型类发生变化(增、减属性)时,克隆的对象也会做出相应的改变。
4、对已经创建好的类进行改造,使其支持克隆时需要修改源代码,这就是违背了ocp(开闭)原则。

最后,如果觉得该篇文章对你有用,麻烦请扫描关注一下我的公众号。谢谢!
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值