設計模式詳解--原型模式

設計模式詳解--原型模式

是指原型實例指定創建對象的種類,並通過拷貝這些原型創建新的對象。而調用者不需要知道任何創建細節,不調用構造函數

其實就是如何快速構建對象的方法總結,比如簡單工廠將getter setter封裝到某個方法中

JDK提供實現Cloneable接口,實現快速複製。當初在spring配置scope="prototype",、scope="singotn"就是在設置是否原型模式創建對象

屬於創建型模式

適用場景:

1)類初始化消耗資源較多

2)new產生的一個對象需要非常繁瑣的過程(數據準備,訪問權限等)

3)構造函數比較複雜

4)循環體中生產大量對象時

克隆分爲深克隆和淺克隆

⑴淺複製(淺克隆) 

    被複制對象的所有變量都含有與原來的對象相同的值,而所有的對其他對象的引用仍然指向原來的對象。換言之,淺複製僅僅複製所考慮的對象,而不復制它所引用的對象。 Object類提供的方法clone只是拷貝本對象,就是在實體類中實現cloneable接口然後重寫clone()方法即可,其對象內部的數組、引用對象等都不拷貝,還是指向原生對象的內部元素地址

我們這裏手寫一個接口模擬jdk原生的拷貝

先寫一個代替clonable的接口Prototype

1.

public interface Prototype{
    Prototype clone();
}

2.然後再寫一個實體類來實現這個接口並重寫clone方法

public class ConcretePrototypeA implements Prototype {

    private int age;
    private String name;
    private List 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 getHobbies() {
        return hobbies;
    }

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

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

}

可以看到我們這裏clone方法就是new一個對象然後把已有的值賦給它。

3.寫一個客戶端來代理原型模式(也可以不寫直接在測試類中調用)

public class Client {

    private Prototype prototype;

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

}

4.寫一個測試類看看兩個對象中的某個元素的地址是否一致

public class PrototypeTest {

    public static void main(String[] args) {

        // 創建一個具體的需要克隆的對象
        ConcretePrototypeA concretePrototype = new ConcretePrototypeA();
        // 填充屬性,方便測試
        concretePrototype.setAge(18);
        concretePrototype.setName("prototype");
        List hobbies = new ArrayList<String>();
        concretePrototype.setHobbies(hobbies);
        System.out.println(concretePrototype);

        // 創建Client對象,準備開始克隆
        Client client = new Client(concretePrototype);
        ConcretePrototypeA concretePrototypeClone = (ConcretePrototypeA) client.startClone(concretePrototype);
        System.out.println(concretePrototypeClone);

        System.out.println("克隆對象中的引用類型地址值:" + concretePrototypeClone.getHobbies());
        System.out.println("原對象中的引用類型地址值:" + concretePrototype.getHobbies());
        System.out.println("對象地址比較:"+(concretePrototypeClone.getName() == concretePrototype.getName()));


    }
}

 

6.輸出結果截圖

可以看到name的屬性複製是淺克隆,地址是一致的

⑵深複製(深克隆) 

      被複制對象的所有變量都含有與原來的對象相同的值,除去那些引用其他對象的變量。那些引用其他對象的變量將指向被複制過的新對象,而不再是原有的那些被引用的對象。換言之,深複製把要複製的對象所引用的對象都複製了一遍。 

我們以孫悟空拔下三根救命毫毛可以變出多個猴子猴孫爲例

1.首先寫一個猴子的實體父類

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

}

2.然後再寫一個金箍棒的實體,因爲千變萬化的孫猴子不可能用一個金箍棒吧,所以這裏要實現深克隆,實現serializable接口

public class JinGuBang implements Serializable {
    public float h = 100;
    public float d = 10;

    public void big(){
        this.d *= 2;
        this.h *= 2;
    }

    public void small(){
        this.d /= 2;
        this.h /= 2;
    }
}

3.寫一個齊天大聖的實體

public class QiTianDaSheng extends Monkey implements Cloneable,Serializable {

    public JinGuBang jinGuBang;

    public  QiTianDaSheng(){
        //只是初始化
        this.birthday = new Date();
        this.jinGuBang = new JinGuBang();
    }

    @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);

            QiTianDaSheng copy = (QiTianDaSheng)ois.readObject();
            copy.birthday = new Date();
            return copy;

        }catch (Exception e){
            e.printStackTrace();
            return null;
        }

    }


    public QiTianDaSheng shallowClone(QiTianDaSheng target){

        QiTianDaSheng qiTianDaSheng = new QiTianDaSheng();
        qiTianDaSheng.height = target.height;
        qiTianDaSheng.weight = target.height;

        qiTianDaSheng.jinGuBang = target.jinGuBang;
        qiTianDaSheng.birthday = new Date();
        return  qiTianDaSheng;
    }


}

這個類要同時實現clonable和serializable接口,我在這個類中寫了兩個方法一個是深克隆一個是淺克隆。

4.編寫測試類

public class DeepCloneTest {

    public static void main(String[] args) {

        QiTianDaSheng qiTianDaSheng = new QiTianDaSheng();
        try {
            QiTianDaSheng clone = (QiTianDaSheng)qiTianDaSheng.clone();
            System.out.println("深克隆:" + (qiTianDaSheng.jinGuBang == clone.jinGuBang));
        } catch (Exception e) {
            e.printStackTrace();
        }

        QiTianDaSheng q = new QiTianDaSheng();
        QiTianDaSheng n = q.shallowClone(q);
        System.out.println("淺克隆:" + (q.jinGuBang == n.jinGuBang));


    }
}

5.運行結果

可以發現深克隆的結果是產生了不同的對象,深克隆成功

(3)總結

深克隆可能會產生單例被破壞的情況,與單例唱反調。所以如果不想讓單例被破壞可以不實現cloneable

缺點:

必須配備克隆(或者可拷貝)方法

對克隆複雜對象或對克隆出的對象進行復雜改造時,容易帶來風險

深拷貝、淺拷貝要運用得當

原型模式的介紹就到這裏啦,如有不當之處,煩請流言指正。下一篇,設計模式詳解--代理模式

 

 

 

 

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