【菜鳥學院】精通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接口
在這裏插入圖片描述

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