创建型设计模式-原型模式

Posted vbirdbest

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了创建型设计模式-原型模式相关的知识,希望对你有一定的参考价值。

用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型对象相同的新对象。

  • 抽象原型类:规定了具体原型对象必须实现的clone()方法。JDK已经提供了java.lang.Cloneable抽象原型类。

  • 具体原型:实现抽象原型类的clone()方法,它是被复制的对象。

  • 访问类:使用具体原型类的clone()方法来复制新的对象。

  • 浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,扔指向原有属性锁指向的对象的内存地址。如JDK中的Object提供的clone()方法。

  • 深克隆:创建一个新对象,属性中应用的其他对象也会被克隆,不再指向原有对象的地址。

public class Object 
	protected native Object clone() throws CloneNotSupportedException;


public interface Cloneable 

使用new关键字创建对象会调用构造方法,使用clone()方法克隆对象并不会调用构造方法。

什么场景使用原型模式

  • 对象的创建非常复杂,可以使用原型模式快捷创建对象
  • 对性能和安全要求比较高。可以按照我们自己的逻辑来创建对象。

浅克隆实现方式

方式一:java.lang.Cloneable接口

@Data
@RequiredArgsConstructor
@AllArgsConstructor
public class Attribute 
    private int age;

实现JDK自带的java.lang.Cloneable接口。

@Data
public class Sheep implements Cloneable 
    private String name;
    private Attribute attribute;


    public Sheep() 
        System.out.println("Sheep构造函数");
    

    @Override
    protected Sheep clone() throws CloneNotSupportedException 
        System.out.println("开始克隆羊");
        return (Sheep)super.clone();
    

public class Client 
    public static void main(String[] args) throws Exception 
        Attribute attribute = new Attribute(2);

        Sheep prototype = new Sheep();
        prototype.setName("原型羊");
        prototype.setAttribute(attribute);

        Sheep copy = prototype.clone();
        copy.setName("多莉(Dolly)");
        attribute.setAge(3);

        // 原型羊:3
        System.out.println(prototype.getName() +":"+prototype.getAttribute().getAge());
        // 多莉(Dolly):3
        System.out.println(copy.getName()+ ":" + copy.getAttribute().getAge());
    

java.lang.Cloneable#clone()方法就是浅拷贝,所以当拷贝对象的时候只是拷贝对象的内存地址,所以一旦修改原型中的引用对象的属性,那么拷贝出来的对象也会跟着改变。

方式二:BeanUtils.copyProperties(Object source, Object target)

注意事项:

  • BeanUtils.copyProperties() 属于浅拷贝,不同的是它也能拷贝两个不同类型的对象。浅拷贝注意事项
    • 如果对象都是基本类型那么随便使用。
    • 如果对象中的属性有引用类型,只要不去修改对象属性的字段也没什么问题。
    • target 中的存在的属性,source中一定要有,但是source中可以有多余的属性。
    • target、source中的属性要名字相同,才能被赋值,不然的话需要手动赋值。
    • Spring的BeanUtils的copyProperties方法需要对应的属性有getter/setter和无参构造方法。
public class Main 
    public static void main(String[] args) 
        Sheep prototype = new Sheep();
        prototype.setName("原型羊");
        prototype.setAttribute(new Attribute(2));

        Sheep target = new Sheep();
        BeanUtils.copyProperties(prototype, target);
        target.getAttribute().setAge(3);

        System.out.println(prototype.getName() +":"+prototype.getAttribute().getAge());
        System.out.println(target.getName()+ ":" + target.getAttribute().getAge());
    

深拷贝实现

方案一:通过对象序列化实现(每个对象都要实现序列化接口Serializable)。

@Data
@RequiredArgsConstructor
@AllArgsConstructor
public class Attribute implements Serializable 
    private int age;


@Data
public class Sheep implements Serializable 
    private String name;
    private Attribute attribute;

public class Client 
    public static void main(String[] args) throws Exception 
        Attribute attribute = new Attribute(2);

        Sheep prototype = new Sheep();
        prototype.setName("原型羊");
        prototype.setAttribute(attribute);

        // 对象序列化到文件
        String file = "/Users/mengday/Downloads/oos.txt";
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file));
        oos.writeObject(prototype);
        oos.close();

        // 序列化文件转对象
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
        Sheep copy = (Sheep)ois.readObject();
        copy.getAttribute().setAge(3);

        // 原型羊:2
        System.out.println(prototype.getName() +":"+prototype.getAttribute().getAge());
        // 多莉(Dolly):3
        System.out.println(copy.getName()+ ":" + copy.getAttribute().getAge());
    

以上是关于创建型设计模式-原型模式的主要内容,如果未能解决你的问题,请参考以下文章

05 创建型原型模式 理解克隆对象~

设计模式:原型模式

8创建型模式之原型模式

设计模式——创建型模式之原型模式

从零开始学习Java设计模式 | 创建型模式篇:原型模式

从零开始学习Java设计模式 | 创建型模式篇:原型模式