天天看點

設計模式之原型模式【設計模式】

最近買了一本設計模式的書籍看了看,發現自己對設計模式沒有什麼概念,同時,在看某些設計模式的時候發現在項目中應用了不少,但是是哪個設計模式卻說不明白!!

不扯皮,今天記錄的是原型模式,java中天然支援原型模式,也就是在jdk層面就支援這個了(clone),代理模式也是(Proxy)。

概念

用原型執行個體制定建立對象的種類,并通過拷貝這些原型并建立新的對象,相當于是拷貝一份副本

看到這個概念的時候我不禁想起了java的Object類的clone方法麼? 難怪java天然支援拷貝模型。話說回來,在java中如果實作克隆對象,要有兩個标準

  1. 必須要實作人家自帶的Cloneable接口
  2. 必須要重寫clone方法。

文字太幹燥了,直接上栗子:

import lombok.Data;
@Data
public class User implements Cloneable{

    private String name ;

    private int age;

    private int sex;

    @Override
    protected Object clone() {
        try {
         // 這裡是重寫clone方法的邏輯
            User    user = (User) super.clone();
            return  user;

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

複制

再來上一個測試類:

public class Client {

    public static void main(String[] args) {
        User user = new User();
        user.setAge();
        User cloneUser = (User)user.clone();
        System.out.println(cloneUser);
    }
}
####得到結果 ::User(name=null, age=18, sex=0)
           

複制

結果: 拷貝成功

原理: 因為需要實作cloneable接口,這個接口它是一個标記接口,沒有任何方法(跟Serializable一樣),jvm用來對标記的對象執行它的clone方法,然後進行記憶體二進制流的拷貝,是以性能會比new好很多。

這裡問題又來了,如果你克隆的對象裡面引用了其他對象呢??試試?

@Data
public class User implements Cloneable{
    //很多根頭發
    private ArrayList hairs = new ArrayList();

    private String name ;

    private int age;

    private int sex;

    @Override
    protected Object clone() {
        try {
            User    user = (User) super.clone();
            return  user;

        }catch (CloneNotSupportedException e){
            e.printStackTrace();
        }
        return null;
    }
}
public class Client {

    public static void main(String[] args) {
        User user = new User();
        user.setAge();
        user.getHairs().add("aaaa");
        User cloneUser = (User)user.clone();
        cloneUser.getHairs().add("abc");
        System.out.println(cloneUser);
        System.out.println(user);
    }
}
### User(hairs=[aaaa, abc], name=null, age=18, sex=0)
###User(hairs=[aaaa, abc], name=null, age=18, sex=0)
           

複制

翻車了? 沒錯,當對象裡面引用另外一個對象的時候,它這裡其實是引用這個對象的位址,這種拷貝也就是我們常說的淺拷貝,有人說了,String不就是引用的對象麼?注意:String它是jdk提供的不可變對象,它内部是通過final類型的char數組實作的,并沒有實作clone 方法,是以它是可以直接被克隆的,與此相同的,比如int Integer,long Long …等等都是可以直接被拷貝的。

那諸如上面的情況,應該如何修改呢?也就是如何修改為深拷貝呢?

so essy!!!直接對内部的對象引用也執行clone方法

@Override    protected Object clone() {        try {            User    user = (User) super.clone();            user.hairs = (ArrayList) hairs.clone();            return  user;        }catch (CloneNotSupportedException e){            e.printStackTrace();        }        return null;    }            

複制

對,沒錯,這裡我們在點到ArrayList源碼看一下,它是有實作clone方法的

設計模式之原型模式【設計模式】

至此,原型模式的栗子就舉完畢了 !

原型模式使用場景

  1. 在使用多個對象但又不便于建立多個對象所承擔的消耗的時候,可以使用克隆
  2. 通過new一個對象的時候需要很繁瑣的進行資料準備,可以使用原型模式對目前對象進行重複修改(一個對象多個修改者的場景)