设计模式八--原型模式

242 阅读5分钟

这是我参与更文挑战的第21天,活动详情查看: 更文挑战

设计模式

原型模式

原型模式其实就是一种克隆。用原型实例指定创建对象的种类,通过克隆这些原型,创建新的对象。

起初我们有一只羊叫多利,颜色为白色,类型为绵羊,后来我们有钱了,需要引进更多属性和多利一样的羊我们怎么写?

Sheep

package com.wangscaler.prototype;

/**
 * @author wangscaler
 * @date 2021.06.23 17:04
 */
public class Sheep {
    private String name;
    private String color;
    private String type;

    public Sheep(String name, String color, String type) {
        this.name = name;
        this.color = color;
        this.type = type;
    }

    public String getName() {
        return name;
    }

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

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }
}

main

package com.wangscaler.prototype;

/**
 * @author wangscaler
 * @date 2021.06.23 17:04
 */
public class Prototype {
    public static void main(String[] args) {
        Sheep sheep = new Sheep("多利", "白色", "绵羊");
        Sheep sheep1 = new Sheep(sheep.getName(), sheep.getColor(), sheep.getType());
        Sheep sheep2 = new Sheep(sheep.getName(), sheep.getColor(), sheep.getType());
        Sheep sheep3 = new Sheep(sheep.getName(), sheep.getColor(), sheep.getType());
        Sheep sheep4 = new Sheep(sheep.getName(), sheep.getColor(), sheep.getType());
        Sheep sheep5 = new Sheep(sheep.getName(), sheep.getColor(), sheep.getType());
        Sheep sheep6 = new Sheep(sheep.getName(), sheep.getColor(), sheep.getType());
       
    }
}

我们虽然克隆出了一摸一样的羊,但是我们不仅每次都重新初始化对象,还重新获取原始对象的属性,如果对象比较复杂,会大大影响我们的开发效率。我们发现Object类有个方法叫clone,那么我们能不能通过实现Cloneavle接口的clone来达到我们的目的呢?

Sheep1

package com.wangscaler.prototype;

/**
 * @author wangscaler
 * @date 2021.06.23 17:04
 */
public class Sheep1 implements Cloneable {
    private String name;
    private String color;
    private String type;

    public Sheep1(String name, String color, String type) {
        this.name = name;
        this.color = color;
        this.type = type;
    }

    public String getName() {
        return name;
    }

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

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Sheep1 sheep = null;
        sheep = (Sheep1) super.clone();
        return sheep;
    }
}

main

package com.wangscaler.prototype;

/**
 * @author wangscaler
 * @date 2021.06.23 17:04
 */
public class Prototype {
    public static void main(String[] args) throws CloneNotSupportedException {
        Sheep1 sheep = new Sheep1("多利", "白色", "绵羊");
        Sheep1 sheep1 = (Sheep1) sheep.clone();
        Sheep1 sheep2 = (Sheep1) sheep.clone();
        Sheep1 sheep3 = (Sheep1) sheep.clone();
        Sheep1 sheep4 = (Sheep1) sheep.clone();
        Sheep1 sheep5 = (Sheep1) sheep.clone();
        Sheep1 sheep6 = (Sheep1) sheep.clone();

    }
}

我们同样复制出了属性相同的羊。

浅拷贝

  • 对于基本类型的成员变量,会进行值的传递
  • 对于引用类型的变量,会进行引用的传递,即内存地址的传递,此时复制前后的对象其实是同一个内存地址,修改其中一个,另一个也会跟着修改(原因可看我的JAVA易错点1
  • 上述克隆羊中的clone方法就是浅拷贝

深拷贝

  • 基本类型会产生值传递
  • 引用类型变量会申请存储空间,并复制引用类型变量所引用的对象

实现方式

  • 重写clone方法
  • 对象序列化

重写clone的方法

package com.wangscaler.prototype;

import java.io.Serializable;

/**
 * @author wangscaler
 * @date 2021.06.24 10:14
 */
public class DeepClone implements Serializable,Cloneable {
    private static final long serialVersionID = 1L;
    private String name;
    private String aclass;

    public DeepClone(String name, String aclass) {
        this.name = name;
        this.aclass = aclass;
    }

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

DeeProtoTypeClone

package com.wangscaler.prototype;

import java.io.Serializable;

/**
 * @author wangscaler
 * @date 2021.06.24 10:14
 */
public class DeeProtoTypeClone implements Serializable, Cloneable {
    private String name;
    private DeepClone deepClone;

    public DeeProtoTypeClone() {
        super();
    }

    public String getName() {
        return name;
    }

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

    public DeepClone getDeepClone() {
        return deepClone;
    }

    public void setDeepClone(DeepClone deepClone) {
        this.deepClone = deepClone;
    }

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

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Object deep = null;
        deep = super.clone();
        DeeProtoTypeClone deeProtoTypeClone = (DeeProtoTypeClone) deep;
        deeProtoTypeClone.deepClone = (DeepClone) deepClone.clone();
        return deeProtoTypeClone;
    }
}

main

package com.wangscaler.prototype;

/**
 * @author wangscaler
 * @date 2021.06.23 17:04
 */
public class Prototype {
    public static void main(String[] args) throws CloneNotSupportedException {
        DeeProtoTypeClone deeProtoTypeClone = new DeeProtoTypeClone();
        deeProtoTypeClone.setName("乾隆");
        DeepClone deepClone = new DeepClone("纪晓岚", "清官");
        deeProtoTypeClone.setDeepClone(deepClone);
        DeeProtoTypeClone deeProtoTypeClone1 = (DeeProtoTypeClone) deeProtoTypeClone.clone();
        DeeProtoTypeClone deeProtoTypeClone2 = (DeeProtoTypeClone) deeProtoTypeClone.clone();
        System.out.println(deeProtoTypeClone.toString());
        System.out.println(deeProtoTypeClone1.toString());
        System.out.println(deeProtoTypeClone2.toString());
    }
}

执行结果

DeeProtoTypeClone{name='乾隆', deepClone=com.wangscaler.prototype.DeepClone@1540e19d}
DeeProtoTypeClone{name='乾隆', deepClone=com.wangscaler.prototype.DeepClone@677327b6}
DeeProtoTypeClone{name='乾隆', deepClone=com.wangscaler.prototype.DeepClone@14ae5a5}

我们可以发现和浅拷贝不同的是,我们成功的将DeepClone对象复制出了多个

对象序列化

DeeProtoTypeClone添加序列化的方法

package com.wangscaler.prototype;

import java.io.*;

/**
 * @author wangscaler
 * @date 2021.06.24 10:14
 */
public class DeeProtoTypeClone implements Serializable, Cloneable {
    private String name;
    private DeepClone deepClone;

    public DeeProtoTypeClone() {
        super();
    }

    public String getName() {
        return name;
    }

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

    public DeepClone getDeepClone() {
        return deepClone;
    }

    public void setDeepClone(DeepClone deepClone) {
        this.deepClone = deepClone;
    }

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

//    @Override
//    protected Object clone() throws CloneNotSupportedException {
//        Object deep = null;
//        deep = super.clone();
//        DeeProtoTypeClone deeProtoTypeClone = (DeeProtoTypeClone) deep;
//        deeProtoTypeClone.deepClone = (DeepClone) deepClone.clone();
//        return deeProtoTypeClone;
//    }

    public Object deepClone() {
        ObjectInputStream objectInputStream = null;
        ObjectOutputStream objectOutputStream = null;
        ByteArrayInputStream byteArrayInputStream = null;
        ByteArrayOutputStream byteArrayOutputStream = null;
        try {
            byteArrayOutputStream = new ByteArrayOutputStream();
            objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
            objectOutputStream.writeObject(this);
            byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
            objectInputStream = new ObjectInputStream(byteArrayInputStream);
            DeeProtoTypeClone deeProtoTypeClone = (DeeProtoTypeClone) objectInputStream.readObject();
            return deeProtoTypeClone;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            try {
                objectInputStream.close();
                objectOutputStream.close();
                byteArrayInputStream.close();
                byteArrayOutputStream.close();
            } catch (Exception e1) {
                e1.printStackTrace();
            }
        }

    }
}

也是可以实现的,推荐使用第二种方式。

源码中的原型模式

spring 中bean的创建

<bean id="id01" class="com.wangscaler.bean.Person" scope="prototype"/>

我们给他指定为原型模式。当我们通过getBean()获取bean对象时,我们可以发现,创建出来的对象的属性是一样的。

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
Object bean =applicationContext.getBean("id01");
Object bean1 =applicationContext.getBean("id01");
System.out.println("bean");
System.out.println("bean1");

当我们点进去getBean的源码,我们发现

public Object getBean(String name) throws BeansException {
    this.assertBeanFactoryActive();
    return this.getBeanFactory().getBean(name);
}

这里调用了getBeanFactory这个方法,点进去我们发现

private DefaultListableBeanFactory beanFactory;
public final ConfigurableListableBeanFactory getBeanFactory() {
    synchronized(this.beanFactoryMonitor) {
        if (this.beanFactory == null) {
            throw new IllegalStateException("BeanFactory not initialized or already closed - call 'refresh' before accessing beans via the ApplicationContext");
        } else {
            return this.beanFactory;
        }
    }
}

这里使用了synchronized同步来确保线程安全的返回这个工厂。

我们进到上一步的getBean方法里发现

public Object getBean(String name) throws BeansException {
    return this.doGetdBean(name, (Class)null, (Object[])null, false);
}

在这里调用了doGetdBean,继续进入

protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException {
    String beanName = this.transformedBeanName(name);
    Object sharedInstance = this.getSingleton(beanName);
 
            if (mbd.isSingleton()) {
                sharedInstance = this.getSingleton(beanName, () -> {
                    try {
                        return this.createBean(beanName, mbd, args);
                    } catch (BeansException var5) {
                        this.destroySingleton(beanName);
                        throw var5;
                    }
                });
                bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
            } else if (mbd.isPrototype()) {
                var11 = null;
 
                Object prototypeInstance;
                try {
                    this.beforePrototypeCreation(beanName);
                    prototypeInstance = this.createBean(beanName, mbd, args);
                } finally {
                    this.afterPrototypeCreation(beanName);
                }
 
                bean = this.getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
            } 

在这里我们发现了先判断是否是单例模式 if (mbd.isSingleton())然后判断 else if (mbd.isPrototype()) 是否是原型模式,如果是的话,会调用createBean创建原型实例。

总结

使用原型模式,可以提高我们的效率,无需初始化对象,而且当对象的属性发生变化时,通过克隆也可以直接复制出变化后的对象。当对象比较复杂时或者我们不知道运行时对象的参数时,推荐使用原型模式。

参考资料

  • [JAVA设计模式](