原型模式

定義

通過給出一個原型對象來指明所要創建的對象的類型,然後用複製這個原型對象的辦法創建出更多同類型的對象。這是原型模式的用意。

原型模式的結果有兩種,一種是淺複製,一種是深複製。

淺複製

用Java方式實現淺複製,被複制對象必須實現Cloneable接口。

package com.faith.net.prototype.simple;

import java.util.List;

/**
 * 簡歷類
 */
public class Resume implements Cloneable {

    private String name;
    private Integer age;
    private List<String> blogs;

    public Resume() {    }

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

    ···省略getter、setter

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

測試:


package com.faith.net.prototype.simple;

import java.util.ArrayList;
import java.util.List;

/**
 * 測試類
 */
public class CloneTest {

    public static void main(String[] args) throws Exception {

        Resume resume = new Resume("faith", 28);
        List<String> blogs = new ArrayList<String>(2);
        blogs.add("yunqi");
        resume.setBlogs(blogs);

        Resume clone = (Resume) resume.clone();

        System.out.println(clone.getName() + ": " + clone.getAge()); // faith: 28
        System.out.println(clone.getBlogs().get(0)); // yunqi

        blogs.set(0, "csdn");
        System.out.println(clone.getBlogs().get(0)); // csdn
    }
}

修改resume對象的blogs屬性,卻使得clone對象的blogs屬性也同時改變了,能夠看出這裏是淺複製,對於引用對象類型的成員,只是複製了引用地址,而沒有重新生成新的拷貝對象。

深複製

實現深複製就要將深複製的邏輯寫到clone方法中,如下:

package com.faith.net.prototype.simple;

import java.io.*;
import java.util.List;

/**
 * 簡歷類
 */
public class Resume implements Cloneable, Serializable {

    private String name;
    private Integer age;
    private List<String> blogs;

    public Resume() {    }

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

    ···省略getter、setter

    @Override
    protected Object clone() {
        try{
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(this);

            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bis);

            Resume clone = (Resume)ois.readObject();
            clone.age = this.age + 1;

            return clone;
        }catch (Exception e){
            throw new RuntimeException();
        }
    }
}

測試類:

/**
 * 測試類
 */
public class CloneTest {

    public static void main(String[] args) throws Exception {

        Resume resume = new Resume("faith", 28);
        List<String> blogs = new ArrayList<String>(2);
        blogs.add("yunqi");
        resume.setBlogs(blogs);

        Resume clone = (Resume) resume.clone();

        System.out.println(clone.getName() + ": " + clone.getAge()); // faith: 29
        System.out.println(clone.getBlogs().get(0)); // yunqi

        blogs.set(0, "csdn");
        System.out.println(clone.getBlogs().get(0)); // yunqi
    }
}

值得注意的是,這裏的clone方法中的邏輯可以通過多種方式實現,只要能重新創建引用類型對象即可,反序列化方式能夠重新創建List類型對象,所以這裏能夠應用。
還可以使用反射方式,Spring中大量使用了反射實現深複製;或者直接將原對象中的blogs取出,循環遍歷元素並添加到新的List對象中。

Spring中的原型模式

Spring中的原型模式大都是使用反射實現的。這裏講一下應用場景:

<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" scope="singleton"/>

這裏的scope是用來配置spring bean的作用域,默認爲singleton,Spring IOC容器中只會存在一個共享的bean實例,所有對bean的請求只會返回同一實例。

當scope值爲prototype,即爲原型模式的應用,每一次請求(將其注入到另一個bean,或以程序方式調用容器的getBean()方法)都會產生一個新的bean實例,相當於new操作。

容器在裝配完prototype實例後交給客戶端,隨後就對該prototype實例不聞不問。而清除prototype對象並釋放其所持有的資源,都是客戶端代碼的職責。

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