【菜鸟学院】精通Spring框架——05原型模式

源码:https://github.com/2020GetGoodOffer/springStudy


原型模式的应用场景

来看一段代码:

public void setParam(ExamPaperVo vo){
	ExamPaper examPaper=new ExamPaper();
	//试卷id
	examPaper.setId(vo.getId());
	//试卷剩余时间
	examPaper.setLeaveTime(vo.getLeaveTime());
	//试卷姓名
	examPaper.setName(vo.getName());
	.....
	//省略数十行
}

该代码非常公正,命名也很规范,注释也很全,但是这样的代码属于纯体力劳动,原型模式就能替我们解决这样的问题。
原型模式是指原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象。
原型模式主要适用于以下场景:
(1)类初始化消耗资源较多
(2)使用new生成一个对象需要非常繁琐的过程(数据准备、访问权限等)
(3)构造方法比较复杂
(4)在循环体中产生大量对象

在Spring中,原型模式应用的非常广泛。
例如scope=“prototype”,经常使用的JSON.parseObject()也是一种原型模式。


浅克隆

一个标准的原型模式代码应该是这样设计的,先创建原型Prototype接口:

public interface Prototype {
    
    Prototype clone();
}

创建具体需要克隆的类ConcretePrototypeA:

public class ConcretePrototypeA implements Prototype {
    
    private int age;
    private String name;
    private List<String> hobbies;

    public int getAge() {
        return age;
    }

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

    public String getName() {
        return name;
    }

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

    public List<String> getHobbies() {
        return hobbies;
    }

    public void setHobbies(List<String> hobbies) {
        this.hobbies = hobbies;
    }

    @Override
    public Prototype clone() {
        ConcretePrototypeA concretePrototypeA=new ConcretePrototypeA();
        concretePrototypeA.setAge(this.age);
        concretePrototypeA.setName(this.name);
        concretePrototypeA.setHobbies(this.hobbies);
        return concretePrototypeA;
    }
}

创建Client类:

public class Client {
    private Prototype prototype;
    
    public Client(Prototype prototype){
        this.prototype=prototype;
    }
    
    public Prototype startClone(Prototype prototype){
        return prototype.clone();
    }
}

测试代码:

public static void main(String[] args) {
        //创建需要克隆的对象
        ConcretePrototypeA concretePrototypeA=new ConcretePrototypeA();
        //填充属性
        concretePrototypeA.setAge(18);
        concretePrototypeA.setName("prototype");
        concretePrototypeA.setHobbies(new ArrayList<>());
        System.out.println(concretePrototypeA);

        //克隆
        Client client=new Client(concretePrototypeA);
        ConcretePrototypeA concretePrototypeA1= (ConcretePrototypeA) client.startClone();
        System.out.println(concretePrototypeA1);

        System.out.println("克隆对象中引用类型地址:"+concretePrototypeA1.getHobbies().hashCode());
        System.out.println("原对象中引用类型地址:"+concretePrototypeA.getHobbies().hashCode());
    }

运行结果:
在这里插入图片描述
可以看出引用地址是相同的,意味着复制的不是值,而是引用的地址。这样的话如果修改任意一个对象的属性值,另一个对象的hobbies值都会改变。
这就是浅克隆,浅克隆只是完整复制了值类型数据,没有赋值引用对象。换言之所有引用对象仍然指向原来的对象。


深克隆

我们换一个场景,以孙悟空为例:
原型猴子类:

public class Monkey {
    public int height;
    public int weight;
    public Date birthday;
}

创建引用对象金箍棒类:

public class Bang implements Serializable {
    public float h=100;
    public float d=10;
    
    public void big(){
        this.h*=2;
        this.d*=2;
    }
    
    public void small(){
        this.h/=2;
        this.d/=2;
    }
}

创建具体的孙悟空类:

public class SunWuKong extends Monkey implements Cloneable, Serializable {
    
    public Bang bang;
    public SunWuKong(){
        this.birthday=new Date();
        this.bang=new Bang();
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return this.deepClone();
    }
    
    public Object deepClone(){
        try {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(this);

            ByteArrayInputStream bis=new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bis);
            
            SunWuKong copy= (SunWuKong) ois.readObject();
            copy.birthday=new Date();
            return copy;
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }
    
    public SunWuKong shallowClone(SunWuKong target){
        SunWuKong sunWuKong = new SunWuKong();
        sunWuKong.height=target.height;
        sunWuKong.weight=target.weight;
        sunWuKong.birthday=new Date();
        sunWuKong.bang=target.bang;
        return sunWuKong;
    }
}

测试代码:

public class Test {

    public static void main(String[] args) {
        SunWuKong sunWuKong = new SunWuKong();
        try{
            SunWuKong clone= (SunWuKong) sunWuKong.clone();
            System.out.println("深克隆:"+(sunWuKong.bang==clone.bang));
        }catch (Exception e){
            e.printStackTrace();
        }
        SunWuKong sunWuKong1 = new SunWuKong();
        SunWuKong sunWuKong2 = sunWuKong1.shallowClone(sunWuKong1);
        System.out.println("浅克隆:"+(sunWuKong1.bang==sunWuKong2.bang));
    }
}

运行结果:
在这里插入图片描述


克隆破坏单例模式

如果我们克隆的对象是单例对象,那么意味着深克隆会破坏单例模式。
实际上防止克隆破坏单例模式的思路很简单,禁止深克隆即可。
要么我们的单例类不实现Cloneable接口,要么我们重写clone方法,在clone方法中返回单例对象即可,具体代码如下:

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

clone方法的源码

常用的ArrayList实现了Cloneable接口
在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章