原型模式【创建型模式】

文章首发个人博客:https://www.xdx97.com/article/703593125049794560

一、引出原型模式

如果我们有一个类(sheep),它里面有两个属性,名称(name),年龄(age)。现在我们有一个它的实例(s1),我们需要按照这个实例的属性再去创建两个对象。


1、Sheep

@Data
public class Sheep {

    private String name;

    private Integer age;

    Sheep(String name,Integer age){
        this.name = name;
        this.age = age;
    }
}

2、main

public class Main {

    public static void main(String[] args) {
        Sheep s1 = new Sheep("多利",1);

        Sheep s2 = new Sheep(s1.getName(),s1.getAge());
        Sheep s3 = new Sheep(s1.getName(),s1.getAge());
    }
}

总结:

  • 这样写看起来还好不麻烦也没有问题
  • 但是如果这类里面有100个属性呢?(我们写的代码会很多)
  • 或者我们现在对这个类再添加几个属性呢?(我们需要修改的地方很多)



二、原型模式

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

也就是我们使用clone方法进行对象的创建,我们对上面的 sheep 类新增clone方法。

1、改造后的sheep类

import lombok.Data;
import lombok.ToString;

@Data
@ToString
public class Sheep implements Cloneable{

    private String name;

    private Integer age;

    Sheep(String name,Integer age){
        this.name = name;
        this.age = age;
    }


    @Override
    protected Sheep clone()  {
        Object obj = null;
        try {
            obj = super.clone();
        }catch (Exception e){
            e.printStackTrace();
        }
        return (Sheep)obj;
    }
}

2、main 测试

public class Main {

    public static void main(String[] args) {
        Sheep s1 = new Sheep("多利",1);

        Sheep s2 = s1.clone();
        Sheep s3 = s1.clone();

        System.out.println(s1 == s2);   // false
        System.out.println(s2 == s3);   // false
    }
}

总结

  • 使用clone方法去创建对象,就解决上面的问题了。
  • 但其我们的clone方法是一个浅拷贝,如果类里面有引入属性,那么创建出来的对象的引用都是指向一个的。

我们对 浅拷贝 进行一个测试

1、在上面的 sheep 类里面新增一个 属性

 private Sheep friend;

2、测试

public class Main {

    public static void main(String[] args) {
        Sheep s1 = new Sheep("多利",1);
        s1.setFriend(new Sheep("小道仙",2));

        Sheep s2 = s1.clone();
        Sheep s3 = s1.clone();

        System.out.println(s1.getFriend().getName() + " " + s2.getFriend().getName() + " " + s3.getFriend().getName());

        s2.getFriend().setName("王哈哈");

        System.out.println(s1.getFriend().getName() + " " + s2.getFriend().getName() + " " + s3.getFriend().getName());
    }

    // 打印结果如下
    小道仙 小道仙 小道仙
    王哈哈 王哈哈 王哈哈
}



三、浅拷贝、深拷贝

浅拷贝 :在拷贝对象的时候对引用类型,没有从新创建一个对象,而是指向之前对象的引用。

深拷贝 :解决浅拷贝存在的问题,我们去为引用类型创建一个新的对象。


我们使用深拷贝去解决浅拷贝的问题

方式一:使用多次clone

@Override
protected Sheep clone()  {
    Sheep obj = null;
    try {
        obj = (Sheep)super.clone();
        if (this.getFriend() != null){
            Sheep clone = this.getFriend().clone();
            obj.setFriend(clone);
        }
    }catch (Exception e){
        e.printStackTrace();
    }
    return obj;
 }
  • 从上面的代码我们可以看到所谓多次克隆,也就是对引用类型再进行一次克隆。
  • 优点:它写起来和理解都很简单。
  • 缺点:如果引用类型过多那么写起来很麻烦,后续如果 新增/删除 引用类型,我们还需要修改 clone 方法。

方式二:使用序列化和反序列化 (注:需要实现序列化接口 implements Serializable)

    @Override
    protected Sheep clone()  {

        Sheep obj = null;

       // 创建流对象
        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);
            obj = (Sheep)ois.readObject();

            return obj;
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }finally {
            // 关闭流
            try {
                bos.close();
                oos.close();
                bis.close();
                ois.close();
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
  • 使用序列化优势很明显,后续 新增/删除 引用类型,我们不需要修改 clone 方法,只不过写起来稍微复杂一点。但是推荐使用这个。

两种方法的测试代码如下,结果也都一样

public class Main {

    public static void main(String[] args) {
        Sheep s1 = new Sheep("多利",1);
        s1.setFriend(new Sheep("小道仙",2));

        Sheep s2 = s1.clone();
        Sheep s3 = s1.clone();

        System.out.println(s1.getFriend().getName() + " " + s2.getFriend().getName() + " " + s3.getFriend().getName());

        s2.getFriend().setName("王哈哈");

        System.out.println(s1.getFriend().getName() + " " + s2.getFriend().getName() + " " + s3.getFriend().getName());
        
        // 打印结果
        小道仙 小道仙 小道仙
        小道仙 王哈哈 小道仙
    }
}




在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/Tomwildboar/article/details/105750670