java23種設計模式——四、原型模式

codermy發表於2020-08-27

原始碼在我的githubgitee中獲取

目錄

java23種設計模式—— 一、設計模式介紹
java23種設計模式—— 二、單例模式
java23種設計模式——三、工廠模式
java23種設計模式——四、原型模式

java23種設計模式——四、原型模式

這種模式是實現了一個原型介面,該介面用於建立當前物件的克隆。當直接建立物件的代價比較大時,則採用這種模式。例如,一個物件需要在一個高代價的資料庫操作之後被建立。我們可以快取該物件,在下一個請求時返回它的克隆,在需要的時候更新資料庫,以此來減少資料庫呼叫。

舉個例子,就是當我們需要給電腦安裝win10系統時需要去官網上下載win10系統的安裝包。而安裝包的大小都是很耗時的,還需要另一臺電腦來操作。如果我們下載了一個安裝包放在我們的u盤裡,之後需要安裝win10時是不是就省去了中間尋找,下載等時間呢

原型模式的克隆分為淺克隆和深克隆,Java 中的 Object 類提供了淺克隆的 clone() 方法,具體原型類只要實現 Cloneable 介面就可實現物件的淺克隆,這裡的 Cloneable 介面就是抽象原型類。其程式碼如下

淺克隆

新建一個實體類Sheep實現Cloneable 介面,重寫clone()方法

/**
 * @author codermy
 * @createTime 2020/5/14
 */
public class Sheep implements Cloneable{
    private String name;
    private int age;
    private String sex;

    public String getName() {
        return name;	
    }

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

    public int getAge() {
        return age;
    }

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

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public Sheep(String name, int age, String sex) {
        this.name = name;
        this.age = age;
        this.sex = sex;
    }

    @Override
    public String toString() {
        return "Sheep{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                '}';
    }
    //克隆該例項,使用預設的clone方法
    @Override
    protected Object clone() throws CloneNotSupportedException {
        Sheep sheep =null;
        sheep = (Sheep)super.clone();
        return sheep;
    }
}

測試

/**
 * @author codermy
 * @createTime 2020/5/14
 */
public class Client {
    public static void main(String[] args) throws CloneNotSupportedException {
        Sheep sheep = new Sheep("tom",1,"male");
        Sheep sheep1 = (Sheep)sheep.clone();
        System.out.println(sheep.hashCode());
        System.out.println(sheep);
        System.out.println(sheep1.hashCode());
        System.out.println(sheep1);
        sheep1.setAge(2);
        System.out.println(sheep1);
        System.out.println(sheep);
    }
}

輸出

1163157884
Sheep{name='tom', age=1, sex='male'}
1956725890
Sheep{name='tom', age=1, sex='male'}
Sheep{name='tom', age=2, sex='male'}
Sheep{name='tom', age=1, sex='male'}

在淺克隆中,被複制物件的所有普通成員變數都具有與原來物件相同的值,而所有的對其他物件的引用仍然指向原來的物件。也就是說,淺克隆僅僅複製所考慮的物件,不會複製它所引用的成員物件。

我們先新建一個Pearson類,作為物件屬性

/**
 * @author codermy
 * @createTime 2020/7/24
 */
public class Person implements Cloneable{
    String name;

    public String getName() {
        return name;
    }

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

    public Person(){

    }
    public Person(String name){
        this.name = name;
    }
    
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                '}';
    }    
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

}

我們先給Sheep實體類種新增一個物件屬性

/**
 * @author codermy
 * @createTime 2020/6/16
 */
public class Sheep implements Cloneable {
    private String name;
    private int age;
    private String sex;
    public Person owner;//物件引用

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public Person getOwner() {
        return owner;
    }

    public void setOwner(Person owner) {
        this.owner = owner;
    }

    public Sheep(String name, int age, String sex, Person owner) {
        this.name = name;
        this.age = age;
        this.sex = sex;
        this.owner = owner;
    }

    @Override
    public String toString() {
        return "Sheep{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                ", owner=" + owner +
                '}';
    }

    //克隆該例項,使用預設的clone方法
    @Override
    protected Object clone() throws CloneNotSupportedException {
        Sheep sheep =null;
        sheep = (Sheep)super.clone();
        return sheep;
    }
}

測試類中測試

/**
 * @author codermy
 * @createTime 2020/6/16
 */
public class Client {
    public static void main(String[] args) throws CloneNotSupportedException {
        Person owner = new Person("馬雲");
        Sheep sheep = new Sheep("tom",1,"male", owner);//新建sheep類

        Sheep sheep1 = (Sheep)sheep.clone();//克隆該類
        System.out.println(sheep.hashCode() + "  " + sheep.owner.hashCode());
        System.out.println(sheep + " "+ sheep.owner);
        System.out.println(sheep1.hashCode()+ " " + sheep1.owner.hashCode());
        System.out.println(sheep1 + " " + sheep1.owner);
        sheep1.owner.setName("馬化騰");
        System.out.println(sheep.owner);
        System.out.println(sheep1.owner);
    }
}

輸出

1163157884  1956725890
Sheep{name='tom', age=1, sex='male', owner=Person{name='馬雲'}} Person{name='馬雲'}
356573597 1956725890
Sheep{name='tom', age=1, sex='male', owner=Person{name='馬雲'}} Person{name='馬雲'}
Person{name='馬化騰'}
Person{name='馬化騰'}

我們可以看出淺克隆時物件的引用僅僅是指向了原空間,而並沒有複製物件。

深克隆

在深克隆中,對值型別的成員變數進行值的複製,對引用型別的成員變數也進行引用物件的複製。

自定義clone過程實現深克隆

將上面Sheep類中的clone方法改寫

 
    @Override
    protected Object clone() throws CloneNotSupportedException {
        Sheep sheep =null;
        sheep = (Sheep)super.clone();
        sheep.owner = (Person) sheep.owner.clone();//引用物件的克隆方法
        return sheep;
    }

測試類測試

public class Client {
    public static void main(String[] args) throws CloneNotSupportedException {
        Person owner = new Person("馬雲");
        Sheep sheep = new Sheep("tom",1,"male", owner);

        Sheep sheep1 = (Sheep)sheep.clone();
        System.out.println(sheep.hashCode() + "  " + sheep.owner.hashCode());
        System.out.println(sheep + " "+ sheep.owner);
        System.out.println(sheep1.hashCode()+ " " + sheep1.owner.hashCode());
        System.out.println(sheep1 + " " + sheep1.owner);
        sheep1.owner.setName("馬化騰");
        System.out.println(sheep.owner);
        System.out.println(sheep1.owner);
    }
}

輸出

1163157884  1956725890
Sheep{name='tom', age=1, sex='male', owner=Person{name='馬雲'}} Person{name='馬雲'}
356573597 1735600054
Sheep{name='tom', age=1, sex='male', owner=Person{name='馬雲'}} Person{name='馬雲'}
Person{name='馬雲'}
Person{name='馬化騰'}

這時候我們已經實現了深克隆,但是總覺得有點“淺淺克隆”的意思,如果person類中還有物件引用那不就是。。

禁止套娃

序列化實現深克隆

兩個實體類實現序列化介面

Person類

public class Person implements Serializable {
    String name;

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                '}';
    }

    public Person(String name){
        this.name = name;
    }

}

Sheep類

/**
 * @author codermy
 * @createTime 2020/6/16
 */
public class Sheep implements  Serializable {
    private String name;
    private int age;
    private String sex;
    public Person owner;//物件引用

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public Person getOwner() {
        return owner;
    }

    public void setOwner(Person owner) {
        this.owner = owner;
    }
    public Sheep() {
    }
    public Sheep(String name, int age, String sex, Person owner) {
        this.name = name;
        this.age = age;
        this.sex = sex;
        this.owner = owner;
    }

    @Override
    public String toString() {
        return "Sheep{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                ", owner=" + owner +
                '}';
    }

}

實現

**
 * @author codermy
 * @createTime 2020/7/24
 */
public class Client {
    public static void main(String[] args) throws Exception {
        Person owner = new Person("馬雲");
        Sheep sheep = new Sheep("tom",1,"male", owner);
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(sheep);
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
        Sheep sheep1 =(Sheep) ois.readObject();
        bos.flush();oos.flush();
        bos.close();oos.close();
        ois.close();
        System.out.println("Sheep: " + sheep);
        System.out.println("Sheep1: " + sheep1);
        System.out.println("================================");
        System.out.println("Sheep: " + sheep.hashCode() + "++++++++++" + sheep.owner.hashCode());
        System.out.println("Sheep1: " + sheep1.hashCode() + "++++++++++" + sheep1.owner.hashCode());
        System.out.println("================================");
        sheep1.owner.setName("馬化騰");
        System.out.println("Sheep: " + sheep.owner);
        System.out.println("Sheep1: " + sheep1.owner);
    }
}

輸出

1163157884  1956725890
Sheep{name='tom', age=1, sex='male', owner=Person{name='馬雲'}} Person{name='馬雲'}
356573597 1735600054
Sheep{name='tom', age=1, sex='male', owner=Person{name='馬雲'}} Person{name='馬雲'}
Person{name='馬雲'}
Person{name='馬化騰'}

原型模式的優缺點

優點:原型模式是在記憶體中二進位制流的拷貝,要比new一個物件的效能要好,特別是需要產生大量物件時。

缺點:直接在記憶體中拷貝,建構函式是不會執行的。

相關文章